reified — Kotlin 语言中的关键字,允许在内联函数中访问泛型参数的类型。在普通的泛型中,类型擦除(type erasure)会起作用——类型信息在编译阶段被擦除,但 reified 可以保留它。根据 Kotlin Documentation, 2025,reified 仅在内联函数中工作,因为编译器在内联阶段替换实际类型。
要点
reified — 是内联函数泛型参数的修饰符,它使类型在运行时变为真实(reify 意为“具体化”)。没有 reified,泛型函数内部的类型 T 不可访问——编译器应用类型擦除,擦除所有关于类型的信息。reified 强制编译器在调用位置替换具体类型,使其通过 T::class 和 is 运算符可访问。
根据 Kotlin Survey by Kodee (2024),reified 类型参数是 Kotlin 最受欢迎的十大功能之一——52% 的受访开发人员使用它,主要用于编写泛型工厂、DI 容器和序列化器。Reified 与 Gson、Moshi 和 Kotlinx Serialization 结合使用时特别流行。
从技术上讲,其机制很简单:当调用带有 reified 参数的内联函数时,编译器知道参数的具体类型(Int、String、User),并将其替换到 T 的位置。在字节码中,reified 参数转换为普通的 Class
使用 reified 编写需要在运行时获取类型的泛型函数——创建实例、类型检查、获取 Class
类型擦除(Type erasure)——Java 和 Kotlin 的机制,在编译期间泛型参数的信息被擦除。在字节码中,List
// ❌ 错误:无法检查已擦除类型的实例
fun <T> checkType(value: Any) {
if (value is T) { // 类型擦除 — T 未知
println("类型匹配")
}
}
// ✅ Solution: pass Class as parameter
fun <T> checkTypeWithClass(
value: Any,
clazz: Class<T>
) {
if (clazz.isInstance(value)) {
println("类型匹配")
}
}
在示例中,checkType 由于类型擦除而无法编译——编译器不知道用什么类型替换 T。在 checkTypeWithClass 中,问题通过显式传递 Class
reified 修饰符放在内联函数中的泛型参数之前。函数必须是内联的——编译器必须能够在内联阶段替换具体类型。
inline fun <reified T> isA(value: Any): Boolean {
return value is T
}
fun main() {
println(isA<String>("你好")) // true
println(isA<Int>("你好")) // false
}
在编译期间,调用 isA
如果我们反编译 isA
Reified 最常见的应用——通过 is 运算符进行类型检查。在普通的泛型函数中,value is T 无法编译。使用 reified,它就像普通类一样工作:value is String、value is List
inline fun <reified T> List<Any>.filterByType(): List<T> {
return this.filter { it is T }.map { it as T }
}
val mixed = listOf("a", 1, "b", 2)
val strings = mixed.filterByType<String>() // ["a", "b"]
val ints = mixed.filterByType<Int>() // [1, 2]
扩展函数 filterByType 过滤列表,只留下指定类型的元素。没有 reified,必须编写带 Class
根据数据 Kotlin Coroutines Guide (JetBrains, 2025),reified 类型检查用于 launch 和 async 中传递协程结果的类型,这在大多数情况下避免了显式指定类型。
Reified 提供对 T::class 的访问——对 KClass 的引用,通过 .java 可以获取 Java Class。这为通过反射创建实例、使用序列化器和在运行时获取类注解提供了可能性。
inline fun <reified T> createInstance(): T =
T::class.java.getDeclaredConstructor().newInstance()
// 用法
data class User(val name: String = "default")
val user = createInstance<User>()
// 使用 Gson 进行序列化
inline fun <reified T> Gson.fromJson(json: String): T =
this.fromJson(json, T::class.java)
// 获取注解
inline fun <reified T> hasAnnotation<A>(): Boolean where A : Annotation =
T::class.java.isAnnotationPresent(A::class.java)
Gson 的 fromJson 包装器——在生产中使用 reified 的经典示例。可以写 gson.fromJson
Reified 有局限性。第一——仅在内联函数内部工作。如果函数不能做成内联的(例如,它是递归的或太大),reified 不可用。第二——reified 不能直接与挂起函数一起使用,只能通过内联包装器。
第三——reified 不能完全与参数化类型一起工作。例如,filterByType>() 可能产生意外结果,因为对于参数化类型,reified 只保留原始类型(List),而不指定泛型参数。要完全检查参数化类型,需要使用 TypeToken 进行反射。
| 操作 | 使用 Reified | 不使用 Reified |
|---|---|---|
| value is T | ✅ 有效 | ❌ 编译错误 |
| T::class | ✅ 有效 | ❌ 编译错误 |
| List | ⚠️ 仅原始类型 | ❌ 错误 |
| 创建实例 | ✅ 通过反射 | ❌ 需要 Class |
| 挂起函数 | ❌ 仅通过内联包装器 | ❌ 不适用 |
对于 reified 不可用的场景,使用显式传递 Class
常见问题
编译器在嵌入函数体时将 reified 参数 T 替换为具体类型。如果函数不是内联的,编译器没有地方替换类型——泛型函数的调用通过统一的字节码进行,其中 T 被擦除了。内联为每个类型参数创建单独的字节码副本。
不能,reified 仅适用于函数参数。对于属性,使用带有返回值的 inline fun
Reified 支持可空类型:reified T : Any(非空)和简单的 reified T(可以为空)。对于可空类型,T::class 返回非空版本的类(String? 返回 String::class)。value is T 检查考虑了 null:如果 T = String?,那么 null is T = true。
最小。Reified 不使用反射——编译器在内联阶段替换具体类型。在字节码中,这是对类的直接引用(ldc + checkcast/invokevirtual)。与手动传递 Class
可以,reified 在 Android 中被积极使用。Bundle.getParcelable
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。