引言
先看一段代码:
Kotlin
/**
* 高阶函数:安全解析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 里都有泛型,比如:
Kotlin
fun <T> printType(item: T) {
println(item::class.java)
}
你可能以为能输出类型,但如果你这样调用:
printType(listOf("abc"))
其实有些情况下,程序拿不到 T 的真实类型 。
因为------Kotlin(跟 Java 一样)在运行时会"擦除泛型" (叫做 Type Erasure)。
也就是说:
你在写代码时有类型信息,
但程序运行的时候,这个类型就被"抹掉"了。
二、那"被抹掉"会造成什么问题?
比如你想写一个通用的 JSON 解析函数:
Kotlin
fun <T> parse(json: String): T {
return Gson().fromJson(json, T::class.java)
}
这行其实会报错,因为:
Kotlin 编译器说:"T 是什么我不知道,你给我个 Class 才能解析。"
三、于是 Kotlin 提供了一个魔法:reified
当你在 内联函数(inline function) 里加上 reified,
Kotlin 就会在编译时帮你把类型"保留下来"。
比如:
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:
Kotlin
fun <T> parse(json: String): T {
return Gson().fromJson(json, T::class.java) // ❌ 报错,拿不到 T 的类型
}
✅ 有 reified:
Kotlin
inline fun <reified T> parse(json: String): T {
return Gson().fromJson(json, T::class.java) // ✅ 可以用
}
六、现实中的用法(比如你那段 safeParseJson)
Kotlin
private inline fun <reified T> safeParseJson(
jsonMessage: String,
onSuccess: (T) -> Unit,
noinline onError: ((Exception) -> Unit)? = null
)
这里 reified T 就让你能这样写:
Kotlin
safeParseJson<User>(json) { user ->
println("用户名:${user.name}")
}
不用你手动传 User::class.java,Kotlin 会自动知道 T 是 User。
七、总结一句话
| 特性 | 作用 | 举例 |
|---|---|---|
inline |
函数在编译时展开,提高性能 | inline fun foo() |
reified |
让泛型在运行时还能"知道类型" | inline fun <reified T> parse() |
| 两者一起 | 能在运行时用 T::class、T::class.java |
JSON解析、反射、类型判断等场景 |
要是再用一句生活比喻来记:
平时的泛型函数就像"快递包装盒",标签被撕掉了,你不知道里面是什么;
加上
reified后,就像"盒子外写清楚了名字",打开之前你就知道里面是啥。
一张图表示:

=================================================================
Java 的泛型
一、Java 的泛型是"类型擦除"的
在 Java 中,所有泛型信息在编译后都会被"擦除",只保留原始类型。
例如👇:
java
List<String> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
这两个在运行时其实是一样的类型 ,都只是 ArrayList。
👉 JVM 不知道泛型参数 String 或 Integer,因为:
Java 的泛型是"编译期检查 + 运行期擦除"。
举个例子:
java
public <T> void printType(T item) {
System.out.println(item.getClass());
}
调用:
java
printType("Hello"); // 输出 class java.lang.String
printType(123); // 输出 class java.lang.Integer
看似没问题,但其实函数本身 在运行时并不知道"我这里是 <String> 还是 <Int>",
它只是根据传入的值推断类型。
如果你想在 Java 中手动保留泛型信息,就得像这样:
java
public <T> T fromJson(String json, Class<T> clazz) {
return gson.fromJson(json, clazz);
}
必须额外传 Class<T>,因为 Java 自己无法保留类型。
Kotlin 的 reified 可以在运行时拿到类型
Kotlin 的 reified 是一种编译期技巧 + 语法糖。
Kotlin
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)
✅ 简洁、直观、少传参数。