引言

先看一段代碼:

/**
     * 高階函數:安全解析JSON消息
     * @param jsonMessage JSON消息字符串
     * @param onSuccess 解析成功回調
     * @param onError 解析失敗回調(可選)
     */
    private inline fun <reified T> safeParseJson(
        jsonMessage: String,
        onSuccess: (T) -> Unit,
        noinline onError: ((Exception) -> Unit)? = null
    ) {
        try {
            val result = gson.fromJson(jsonMessage, T::class.java)
            onSuccess(result)
        } catch (e: JsonSyntaxException) {
            Log.e(TAG, "JSON解析失敗 - 類型: ${T::class.simpleName}, error: ${e.message}")
            Log.e(TAG, "問題JSON: ${jsonMessage.take(300)}")
            onError?.invoke(e)
        } catch (e: Exception) {
            Log.e(TAG, "解析異常 - 類型: ${T::class.simpleName}, error: ${e.message}")
            onError?.invoke(e)
        }
    }

<reified T>

這reified 是什麼意思??

kotlin reified

一、先理解問題:泛型為什麼“有時候拿不到類型”?

你知道 Kotlin 和 Java 裏都有泛型,比如:

fun <T> printType(item: T) {
    println(item::class.java)
}

你可能以為能輸出類型,但如果你這樣調用:

printType(listOf("abc"))
 

其實有些情況下,程序拿不到 T 的真實類型
因為——Kotlin(跟 Java 一樣)在運行時會“擦除泛型”(叫做 Type Erasure)。

也就是説:

你在寫代碼時有類型信息,
但程序運行的時候,這個類型就被“抹掉”了。

 二、那“被抹掉”會造成什麼問題?

比如你想寫一個通用的 JSON 解析函數:

fun <T> parse(json: String): T {
    return Gson().fromJson(json, T::class.java)
}

這行其實會報錯,因為:

Kotlin 編譯器説:“T 是什麼我不知道,你給我個 Class 才能解析。”

三、於是 Kotlin 提供了一個魔法:reified

當你在 內聯函數(inline function) 里加上 reified
Kotlin 就會在編譯時幫你把類型“保留下來”。

比如:

inline fun <reified T> parse(json: String): T {
    return Gson().fromJson(json, T::class.java)
}

這樣就能用了!

四、為什麼要 inline + reified 一起用?

因為:

  • inline:讓函數在編譯時“展開”,也就是直接把函數體複製到調用的地方;
  • reified:只有在 inline 的函數裏才能用,因為只有這樣,編譯器才能知道 T 的真實類型是什麼。

所以 reified 必須依附在 inline 上。

五、用例對比:有 vs 沒有 reified

❌ 沒有 reified:
fun <T> parse(json: String): T {
    return Gson().fromJson(json, T::class.java) // ❌ 報錯,拿不到 T 的類型
}

✅ 有 reified:

inline fun <reified T> parse(json: String): T {
    return Gson().fromJson(json, T::class.java) // ✅ 可以用
}

六、現實中的用法(比如你那段 safeParseJson)

private inline fun <reified T> safeParseJson(
    jsonMessage: String,
    onSuccess: (T) -> Unit,
    noinline onError: ((Exception) -> Unit)? = null
)

這裏 reified T 就讓你能這樣寫:

safeParseJson<User>(json) { user ->
    println("用户名:${user.name}")
}

不用你手動傳 User::class.java,Kotlin 會自動知道 T 是 User

七、總結一句話

特性

作用

舉例

inline

函數在編譯時展開,提高性能

inline fun foo()

reified

讓泛型在運行時還能“知道類型”

inline fun <reified T> parse()

兩者一起

能在運行時用 T::classT::class.java

JSON解析、反射、類型判斷等場景

要是再用一句生活比喻來記:

平時的泛型函數就像“快遞包裝盒”,標籤被撕掉了,你不知道里面是什麼;
加上 reified 後,就像“盒子外寫清楚了名字”,打開之前你就知道里面是啥。

一張圖表示:

Kotlin第七講--泛型在Java和Kotlin上的差異_json

=================================================================

Java 的泛型

一、Java 的泛型是“類型擦除”的

在 Java 中,所有泛型信息在編譯後都會被“擦除”,只保留原始類型。

例如👇:

List<String> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();

這兩個在運行時其實是一樣的類型,都只是 ArrayList

👉 JVM 不知道泛型參數 String 或 Integer,因為:

Java 的泛型是“編譯期檢查 + 運行期擦除”。

舉個例子:

public <T> void printType(T item) {
    System.out.println(item.getClass());
}

調用:

printType("Hello"); // 輸出 class java.lang.String
printType(123);     // 輸出 class java.lang.Integer

看似沒問題,但其實函數本身在運行時並不知道“我這裏是 <String> 還是 <Int>”,
它只是根據傳入的值推斷類型。

如果你想在 Java 中手動保留泛型信息,就得像這樣:

public <T> T fromJson(String json, Class<T> clazz) {
    return gson.fromJson(json, clazz);
}

必須額外傳 Class<T>,因為 Java 自己無法保留類型。

Kotlin 的 reified 可以在運行時拿到類型

Kotlin 的 reified 是一種編譯期技巧 + 語法糖

inline fun <reified T> parse(json: String): T {
    return gson.fromJson(json, T::class.java)
}

Kotlin 編譯器會在編譯時直接把 T 的真實類型寫進去(比如 User::class.java)。

所以:

Kotlin 在 inline + reified 的情況下,可以直接拿到類型,不需要像 Java 一樣手動傳。

對比表 

特性

Java 泛型

Kotlin 普通泛型

Kotlin inline + reified

泛型信息

編譯期存在,運行期被擦除

一樣被擦除

✅ 編譯時保留類型

是否能用 T::class.java

❌ 不行

❌ 不行

✅ 可以

是否能直接做 JSON 解析

❌ 需手動傳 Class<T>

❌ 同樣需要

✅ 不用傳,自動識別

編譯方式

普通編譯

普通編譯

inline 展開,保留類型信息

用一句話總結:

Java 的泛型信息在運行時是“丟失”的;
Kotlin 的 reified 泛型能在運行時“記得”自己是誰。

舉例對比

Java 寫法:

User user = parse(json, User.class);
 

Kotlin 沒有 reified 的寫法:

val user = parse(json, User::class.java)
 

Kotlin 有 reified 的寫法:

val user = parse<User>(json)
✅ 簡潔、直觀、少傳參數。