类型擦除与 reified 类型参数
将 reified 与 inline 函数结合,在运行时访问类型信息。
类型擦除与 reified 类型参数 是 CoddyKit 上的免费 Kotlin Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Kotlin Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Kotlin Academy 课程共包含 4 节课。
什么是类型擦除
在运行时,JVM 会擦除泛型类型参数。List<String> 和 List<Int> 最终都只是 List。这限制了对泛型执行运行时类型操作的能力。
fun main() {
val strings: List<String> = listOf("a", "b")
val ints: List<Int> = listOf(1, 2)
println(strings.javaClass == ints.javaClass) // true: both ArrayList
println(strings is List<*>) // OK with star
// println(strings is List<String>) // warning: unchecked cast
}问题:无法检查被擦除的类型
您不能对带有具体泛型类型参数的类型使用 is,因为类型信息在运行时已经消失。
fun checkType(obj: Any) {
// This works:
if (obj is List<*>) println("It's a list of something")
// This doesn't compile cleanly:
// if (obj is List<String>) println("It's a list of strings") // unchecked
}
fun main() {
checkType(listOf("hello"))
checkType(42)
}inline 函数回顾
inline 会在编译时将函数体复制到调用位置。这正是 reified 能够工作的原因——类型参数也会在调用位置被替换。
inline fun <T> myRun(block: () -> T): T = block()
fun main() {
val result = myRun { 42 }
println(result) // 42
// The compiler inlines the lambda body — no object allocation
}reified:在运行时访问 T
将 inline 与 reified 结合,可以在调用位置保留类型参数,从而对 T 使用 is、as 和 ::class。
inline fun <reified T> isInstance(value: Any): Boolean = value is T
fun main() {
println(isInstance<String>("hello")) // true
println(isInstance<Int>("hello")) // false
println(isInstance<List<*>>(listOf())) // true
}获取 T 的 KClass
使用 reified T,您可以在运行时获取 T::class——这在普通泛型函数中通常是不可能的。
inline fun <reified T> classNameOf(): String = T::class.simpleName ?: "Unknown"
fun main() {
println(classNameOf<String>()) // String
println(classNameOf<Int>()) // Int
println(classNameOf<List<*>>()) // List
}使用 reified 进行安全转换
使用 reified 编写类型安全的转换工具,在转换失败时返回 null,而不是抛出异常。
inline fun <reified T> Any?.safeCast(): T? = this as? T
fun main() {
val obj: Any = "Hello"
val s: String? = obj.safeCast<String>()
val i: Int? = obj.safeCast<Int>()
println(s) // Hello
println(i) // null
}使用 reified 解析 JSON
Gson 或 kotlinx.serialization 等库使用 reified 在调用位置推断目标类型,而无需显式传入 Class 对象。
import com.google.gson.Gson
// Simulated without real Gson:
inline fun <reified T> parseJson(json: String): T {
return Gson().fromJson(json, T::class.java)
}
// Usage:
// val user: User = parseJson<User>("""{"name":"Alice"}""")reified 与 filterIsInstance
标准库中的 filterIsInstance<T>() 使用 reified 将列表筛选为特定类型,而无需手动转换。
fun main() {
val mixed: List<Any> = listOf(1, "hello", 2.5, "world", true)
val strings = mixed.filterIsInstance<String>()
val ints = mixed.filterIsInstance<Int>()
println(strings) // [hello, world]
println(ints) // [1]
}非 inline 函数不能使用 reified
reified 只能用于 inline 函数。尝试在没有 inline 的情况下使用它会导致编译错误。
// Error:
// fun <reified T> broken(): String = T::class.simpleName ?: "?"
// Must be inline:
inline fun <reified T> correct(): String = T::class.simpleName ?: "?"
fun main() {
println(correct<Double>()) // Double
}reified 与反射
将 reified 与反射结合,可以动态检查类型的属性或注解,而无需编写多余的 Class 参数。
inline fun <reified T : Any> properties(): List<String> =
T::class.members.map { it.name }
data class Point(val x: Int, val y: Int)
fun main() {
println(properties<Point>()) // [x, y, component1, component2, copy, equals, hashCode, toString]
}实践:类型安全的服务定位器
使用 reified 构建服务定位器,将 KClass 映射到实例,从而无需注解处理器即可实现轻量级 DI。
val registry = mutableMapOf<kotlin.reflect.KClass<*>, Any>()
inline fun <reified T : Any> register(instance: T) { registry[T::class] = instance }
@Suppress("UNCHECKED_CAST")
inline fun <reified T : Any> resolve(): T = registry[T::class] as T
class Logger { fun log(msg: String) = println("[LOG] $msg") }
fun main() {
register(Logger())
resolve<Logger>().log("Service located!")
}reified 的限制
reified 类型参数不能用于非 inline 上下文,不能存储类型供以后使用,也不能传递给那些需要在运行时获取类型的非 inline 函数。
inline fun <reified T> foo() {
val clazz = T::class // OK here
// bar(clazz) // must pass clazz explicitly to non-inline funs
println(clazz.simpleName)
}
fun bar(clazz: kotlin.reflect.KClass<*>) = println(clazz.simpleName)
fun main() { foo<String>() }快速检查
要让类型参数成为 reified,必须满足什么条件?
回顾
类型擦除会在运行时移除泛型类型信息。在 inline 函数上使用 reified,可以在调用位置保留类型参数,从而支持对 T 使用 is、as、::class 和反射,而无需手动传递 Class 对象。
常见问题解答
「类型擦除与 reified 类型参数」课时是免费的吗?
是的 — 「类型擦除与 reified 类型参数」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Kotlin Academy 课程的其余内容,请升级到 CoddyKit PRO。 Kotlin Academy 课程共包含 4 节课。
「类型擦除与 reified 类型参数」这节课中我会学到什么?
将 reified 与 inline 函数结合,在运行时访问类型信息。 你通过在浏览器中直接运行的动手代码来练习 Kotlin Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Kotlin Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Kotlin Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「类型擦除与 reified 类型参数」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Kotlin Academy 课中编写并运行代码吗?
能。每节 Kotlin Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 使用 where 的泛型函数与类型约束
- 声明处型变:in 与 out
- 星号投影与 * 的使用时机
- 类型擦除与 reified 类型参数