使用 reified type parameters ,可以编写一个内联函数,在运行时通过反射与类型参数一起工作:
inline fun <reified T: Any> f() {
val clazz = T::class
// ...
}
但是当 f
使用本身是泛型类的参数调用时,似乎无法通过 T::class
获取其实际类型参数:
f<List<Integer>>() // T::class is just kotlin.collections.List
有没有办法通过反射获得具体泛型的实际类型参数?
最佳答案
由于 type erasure , 实际的泛型参数无法通过 T::class
泛型类的标记。一个类的不同对象必须具有相同的类标记,这就是它不能包含实际泛型参数的原因。
编辑:从 Kotlin 1.3.50 开始,不再需要遵循下面描述的技术来获取具体类型参数的类型信息。相反,您可以使用 typeOf<T>()
关于具体类型参数。此函数是编译器内在函数,编译器通过发出将类型表示构建为 KType
的代码来处理其调用站点。在运行时。因此,必须在编译时知道类型,这是通过类型参数 reified
来确保的。 .
有一种技术叫做 super type tokens如果在编译时知道类型,它可以给出实际的类型参数(由于内联,Kotlin 中的具体泛型也是如此)。
诀窍在于编译器保留从泛型类派生的非泛型类的实际类型参数(它的所有实例都将具有相同的参数,很好的解释here)。可通过 clazz.genericSuperClass.actualTypeArguments
访问它们的 Class<*>
实例。
鉴于所有这些,您可以编写一个这样的 util 类:
abstract class TypeReference<T> : Comparable<TypeReference<T>> {
val type: Type =
(javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0]
override fun compareTo(other: TypeReference<T>) = 0
}
解释于 Jackson TypeReference它使用相同的方法。 Jackson Kotlin 模块 uses it关于具体化的泛型。
之后,在具有具体泛型的内联函数中,TypeReference
需要子类化(object expression 会去),然后是它的 type
可以使用。
例子:
inline fun <reified T: Any> printGenerics() {
val type = object : TypeReference<T>() {}.type
if (type is ParameterizedType)
type.actualTypeArguments.forEach { println(it.typeName) }
}
printGenerics<HashMap<Int, List<String>>>()
:
java.lang.Integer java.util.List<? extends java.lang.String>
https://stackoverflow.com/questions/36253310/