Kotlin reified泛型 和 Java 泛型 区别

引言

先看一段代码:

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 会自动知道 TUser

七、总结一句话

特性 作用 举例
inline 函数在编译时展开,提高性能 inline fun foo()
reified 让泛型在运行时还能"知道类型" inline fun <reified T> parse()
两者一起 能在运行时用 T::classT::class.java JSON解析、反射、类型判断等场景

要是再用一句生活比喻来记:

平时的泛型函数就像"快递包装盒",标签被撕掉了,你不知道里面是什么;

加上 reified 后,就像"盒子外写清楚了名字",打开之前你就知道里面是啥。

一张图表示:

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

Java 的泛型

一、Java 的泛型是"类型擦除"的

在 Java 中,所有泛型信息在编译后都会被"擦除",只保留原始类型。

例如👇:

java 复制代码
List<String> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();

这两个在运行时其实是一样的类型 ,都只是 ArrayList

👉 JVM 不知道泛型参数 StringInteger,因为:

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)

✅ 简洁、直观、少传参数。

相关推荐
CoderYanger6 小时前
C.滑动窗口-求子数组个数-越长越合法——2799. 统计完全子数组的数目
java·c语言·开发语言·数据结构·算法·leetcode·职场和发展
C++业余爱好者6 小时前
Java 提供了8种基本数据类型及封装类型介绍
java·开发语言·python
想用offer打牌6 小时前
RocketMQ如何防止消息丢失?
java·后端·架构·开源·rocketmq
皮卡龙6 小时前
Java常用的JSON
java·开发语言·spring boot·json
利刃大大7 小时前
【JavaSE】十三、枚举类Enum && Lambda表达式 && 列表排序常见写法
java·开发语言·枚举·lambda·排序
float_六七7 小时前
Java反射:万能遥控器拆解编程
java·开发语言
han_hanker7 小时前
java 异常类——详解
java·开发语言
源码获取_wx:Fegn08957 小时前
基于springboot + vue健身房管理系统
java·开发语言·前端·vue.js·spring boot·后端·spring
峥嵘life8 小时前
Android16 EDLA 认证测试CTS问题分析解决
android·java·服务器
Mr1ght8 小时前
为什么 InheritableThreadLocal 在 Spring 线程池中“偶尔”能传递变量?——一次线程池上下文传播的误解
java·spring