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)

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

相关推荐
czhc11400756637 小时前
JAVA111 HashMap Leecode:1 两数之和 3 无重复字符串的长度
java
凌冰_7 小时前
Java Maven+lombok+MySql+HikariCP 操作数据库
java·数据库·maven
武子康7 小时前
Java-165 Neo4j 图论详解 欧拉路径与欧拉回路 10 分钟跑通:Python NetworkX 判定实战
java·数据库·性能优化·系统架构·nosql·neo4j·图论
代码不停8 小时前
Java二分算法题目练习
java·算法
.格子衫.8 小时前
023数据结构之线段树——算法备赛
java·数据结构·算法
Justin_198 小时前
LVS负载均衡集群理论
java·负载均衡·lvs
虾说羊8 小时前
ssm面试题梳理
java·开发语言·rpc
nbsaas-boot8 小时前
什么语言最适合开发 SaaS 系统:从架构视角的全面分析
java·架构
Liudef068 小时前
基于Java的LLM长上下文数据预处理方案:实现128k上下文智能数据选择
java·开发语言·人工智能