问题的历史:
在JVM中,执行期间缺少关于泛型参数的信息(类型擦除)。Kotlin为内联函数提供了reified泛型参数的机制,以便在运行时访问类型T的信息,而不需要像传递Class<T>这样的变通方法。这是Kotlin语言中最强大的工具之一。
问题:
需要编写一个函数,该函数接受某种类型T(泛型)的值,并根据参数的类型执行不同的操作,而无需显式传递java.lang.Class,也无需在Java侧使用反射。经典的类型擦除在执行期间无法得知类型T。
解决方案:
在Kotlin中,允许为内联函数声明带有reified修饰符的泛型参数,这使得可以在函数体内“捕获”类型T,将其作为普通类型处理,进行类型检查,并通过反射创建实例。
代码示例:
inline fun <reified T> isOfType(value: Any): Boolean { return value is T } isOfType<String>(123) // false isOfType<Int>(123) // true
关键特点:
可以在非内联函数中使用reified吗?
不可以!只有内联函数(和内联懒属性)可以具有reified泛型参数。原因是代码在调用处进行替换,仅通过这种方式可以“替换”具体类型到T。
代码示例:
// 编译错误: fun <reified T> errorFun() { } // 正确的变体: inline fun <reified T> okFun() { }
可以在内联reified函数内部获取Class<T>吗?
可以!只需编写T::class.java或T::class。对于编写泛型工厂、解析器和使用反射API,这极为方便。
代码示例:
inline fun <reified T> printType() { println(T::class.java) } printType<String>() // class java.lang.String
可以通过构造函数在reified函数中创建类型T的实例吗?
部分可以。可以使用反射,但无法像在C++或C#中那样直接new T():
inline fun <reified T : Any> makeInstance(): T? { return T::class.constructors.firstOrNull()?.call() }
但这种方法要求T具有无参数构造函数。
带有reified的函数,在代码中调用繁重的反射操作:
inline fun <reified T> foo(): T? = T::class.constructors.firstOrNull()?.call()
优点:
缺点:
使用reified进行通用类型检查或安全转换:
inline fun <reified T> safeCast(value: Any?): T? = value as? T
优点:
缺点: