【问题标题】:Kotlin - TypeReference<T> Cannot obtain Class<*> For Type ArgumentsKotlin - TypeReference<T> 无法获取类型参数的类<*>
【发布时间】:2021-04-10 17:39:49
【问题描述】:

我创建了一个相当于 TypeReference&lt;T&gt; 的 Kotlin,如下所示:

abstract class TypeReference<T> : Comparable<T> {

    val type: Type get() = getGenericType()
    val arguments: List<Type> get() = getTypeArguments()

    final override fun compareTo(other: T): Int {
        return 0
    }

    private fun getGenericType(): Type {
        val superClass = javaClass.genericSuperclass

        check(superClass !is Class<*>) {
            "TypeReference constructed without actual type information."
        }

        return (superClass as ParameterizedType).actualTypeArguments[0]
    }

    private fun getTypeArguments(): List<Type> {
        val type = getGenericType()
        return if (type is ParameterizedType) {
            type.actualTypeArguments.toList()
        } else emptyList()
    }
}

为了获取泛型类型及其参数的Class&lt;*&gt;,我还创建了以下扩展函数(这就是我认为问题所在,因为这是堆栈跟踪失败的地方)。

fun Type.toClass(): Class<*> = when (this) {
    is ParameterizedType -> rawType.toClass()
    is Class<*> -> this
    else -> Class.forName(typeName)
}

我正在像这样进行单元测试:

@Test
fun `TypeReference should correctly identify the List of BigDecimal type`() {

    // Arrange
    val expected = List::class.java
    val expectedParameter1 = BigDecimal::class.java
    val typeReference = object : TypeReference<List<BigDecimal>>() {}

    // Act
    val actual = typeReference.type.toClass()
    val actualParameter1 = typeReference.arguments[0].toClass()

    // Assert
    assertEquals(expected, actual)
    assertEquals(expectedParameter1, actualParameter1)
}

我认为问题在于它抛出的扩展函数else -&gt; Class.forName(typeName)

java.lang.ClassNotFoundException: ?扩展 java.math.BigDecimal

有没有更好的方法来获取TypeClass&lt;*&gt;,即使它们是泛型类型参数?

【问题讨论】:

    标签: kotlin


    【解决方案1】:

    您需要将is WildcardType -&gt; ... 分支添加到您的when-表达式以处理? extends java.math.BigDecimal(Kotlin 等效为out java.math.BigDecimal)、?(Kotlin 等效为*)、? super Integer( Kotlin 等价于 in java.math.Integer):

    fun Type.toClass(): Class<*> = when (this) {
        is ParameterizedType -> rawType.toClass()
        is Class<*> -> this
        is WildcardType -> upperBounds.singleOrNull()?.toClass() ?: Any::class.java
        else -> Class.forName(typeName)
    }
    

    请注意,在此实现中,单个上限类型将被解析为其上限,但所有其他 wildcard types(包括多个上限类型)将被解析为 Class&lt;Object&gt;

    【讨论】:

    • 感谢您的回答。我实际上是这样解决的,虽然我怀疑有一种更优雅的方式将 Type 转换为 Class。
    【解决方案2】:

    https://github.com/pluses/ktypes

    val typeReference = object : TypeReference<List<BigDecimal>>() {}
    
    val superType = typeReference::class.createType().findSuperType(TypeReference::class)!!
    
    println(superType.arguments.first())// List<java.math.BigDecimal>
    println(superType.arguments.first().type?.arguments?.first())// java.math.BigDecimal
    

    【讨论】:

      猜你喜欢
      • 2018-07-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-26
      • 1970-01-01
      相关资源
      最近更新 更多