如何在 Kotlin 中获取具体化泛型参数的实际类型参数? [英] How to get actual type arguments of a reified generic parameter in Kotlin?

查看:60
本文介绍了如何在 Kotlin 中获取具体化泛型参数的实际类型参数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用reified类型参数,可以编写一个有效的内联函数在运行时通过反射带类型参数:

inline fun F() {val clazz = T::class//...}

但是当 f 使用一个本身是泛型类的参数调用时,似乎没有办法通过 T::class 获得它的实际类型参数:

f>()//T::class 只是 kotlin.collections.List

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

解决方案

由于 类型擦除,不能通过泛型类的 T::class 标记获得实际的泛型参数.一个类的不同对象必须具有相同的类标记,这就是它不能包含实际泛型参数的原因.

但是有一种技术叫做超级类型令牌在编译时已知类型的情况下,它可以给出实际的类型参数(由于内联,Kotlin 中的具体泛型确实如此).

<小时>

编辑:从 Kotlin 1.3.50 开始,不再需要按照下面描述的技术来获取具体化类型参数的类型信息.相反,您可以使用 typeOf<T>() 关于具体化的类型参数.

<小时>

诀窍是编译器为从泛型类派生的非泛型类保留实际类型参数(它的所有实例都将具有相同的参数,很好的解释 此处).它们可以通过 Class<*> 实例的 clazz.genericSuperClass.actualTypeArguments 访问.

考虑到所有这些,您可以编写这样的 util 类:

抽象类 TypeReference:Comparable{val 类型:类型 =(javaClass.genericSuperclass 作为 ParameterizedType).actualTypeArguments[0]覆盖 fun compareTo(other: TypeReference) = 0}

Jackson TypeReference 使用相同的方法.Jackson Kotlin 模块 在具体化泛型上使用它.

之后,在具有具体化泛型的内联函数中,TypeReference 需要被子类化(对象表达式会去),然后它的type就可以使用了.

示例:

inline fun 打印泛型(){val type = object : TypeReference() {}.type如果(类型是参数化类型)type.actualTypeArguments.forEach { println(it.typeName) }}

printGenerics>>():

<块引用>

java.lang.Integerjava.util.List

Using reified type parameters, one can write an inline function which works with the type parameter through reflection at runtime:

inline fun <reified T: Any> f() {
    val clazz = T::class
    // ...
}

But when f is called with a parameter which is itself a generic class, there seems to be no way to obtain its actual type arguments through T::class:

f<List<Integer>>() // T::class is just kotlin.collections.List

Is there a way to get actual type arguments of a reified generic through reflection?

解决方案

Due to type erasure, actual generic arguments cannot be obtained through T::class token of a generic class. Different objects of a class must have the same class token, that's why it cannot contain actual generic arguments.

But there is a techinque called super type tokens which can give actual type arguments in case when the type is known at compile time (it is true for reified generics in Kotlin because of inlining).


Edit: Since Kotlin 1.3.50, following the technique described below to get type information for a reified type parameter is no longer necessary. Instead, you can use typeOf<T>() on reified type parameters.


The trick is that the compiler retains actual type arguments for a non-generic class derived from a generic class (all its instances will have the same arguments, good explanation here). They are accessible through clazz.genericSuperClass.actualTypeArguments of a Class<*> instance.

Given all that, you can write a util class like this:

abstract class TypeReference<T> : Comparable<TypeReference<T>> {
    val type: Type = 
        (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0]

    override fun compareTo(other: TypeReference<T>) = 0
}

Explained in Jackson TypeReference which uses the same approach. Jackson Kotlin module uses it on reified generics.

After that, in an inline function with a reified generic, TypeReference needs to be subclassed (an object expression will go), and then its type can be used.

Example:

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>

这篇关于如何在 Kotlin 中获取具体化泛型参数的实际类型参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆