generics - 如何在 Kotlin 中获取具体泛型参数的实际类型参数?

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

相关文章:

android - kotlin 'onCreate' 不会覆盖任何内容

android - 如何混淆我用 kotlin 编码的 sdk(并摆脱元数据)

sorting - Kotlin 最后排序空值

android - 为全屏 Activity 摆脱不必要的根布局

kotlin - 有什么作用?。在作业的左侧使用时在 Kotlin 中的意思是什么?

android - 延迟对 Kotlin 协程进行单元测试

java - 如何在 Kotlin 中初始化一个线程?

kotlin - 在 Kotlin 中对可空整数使用 "greater than"、 "less t

android - 将 LiveData 与数据绑定(bind)一起使用

java - 错误 :Kotlin: Unsupported plugin option: org.