如何在Kotlin中获取具体通用参数的实际类型参数?

时间:2016-03-27 22:13:47

标签: generics reflection kotlin

使用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

有没有办法通过反射获得具体化通用的实际类型参数?

2 个答案:

答案 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)