使用reified type parameters,可以编写一个内联函数,该函数在运行时通过反射与type参数一起使用:
inline fun <reified T: Any> f() {
val clazz = T::class
// ...
}
但是当使用一个本身是泛型类的参数调用f
时,似乎无法通过T::class
获取其实际类型参数:
f<List<Integer>>() // T::class is just kotlin.collections.List
有没有办法通过反射获得具体化通用的实际类型参数?
答案 0 :(得分:35)
由于type erasure,无法通过泛型类的T::class
标记获取实际泛型参数。类的不同对象必须具有相同的类标记,这就是为什么它不能包含实际的泛型参数。
但是有一个名为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中解释使用相同的方法。杰克逊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>
答案 1 :(得分:0)
如果您已在项目中使用gson库(例如json解析)。为此提供了一个类。 com.google.gson.reflect.TypeToken()
。
所以你可以使用类似的东西,
f(object : TypeToken<List<Integer>>() {}.type)