代码之家  ›  专栏  ›  技术社区  ›  Ukonn Ra

Kotlin:如何在运行时的OneClass中获得实际的泛型类型T

  •  1
  • Ukonn Ra  · 技术社区  · 8 年前

    朋友! 我是Kotlin的新手,现在有个问题。 现在我想在OneClass中获得泛型类型T,下面是一些外围代码。

    // IDatabase.kt
    package com.ra
    abstract class IDatabase<T> { }
    
    // Database1.kt
    import kotlin.reflect.jvm.javaType
    open class Database1<T>: IDatabase<T>() {
        val tClass = this::class.supertypes[0].arguments[0].type!!.javaType as Class<T>
    }
    
    // Database2.kt
    import kotlin.reflect.jvm.javaType
    open class Database2<T>: IDatabase<T>() {
        val tClass = this::class.supertypes[0].arguments[0].type!!.javaType as Class<T>
        // just wrap a `object` to confort the user
        companion object {
            fun <T> getInstance(): Database2<T> {
                return object: Database2<T>() {}
            }
        }
    }
    

    下面是主要的函数,在这里我测试了三种获得泛型T的方法,结果都在代码下面的注释中。

    fun main(args: Array<String>) {
        val database1 = Database1<String>()
        println(database1.tClass.canonicalName)
        /**
         * Exception in thread "main" java.lang.ClassCastException:
         * sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.Class
         * at com.ra.Database1.<init>(Database1.kt:6)
         * at com.ra.MainKt.main(Main.kt:4)
         */
    
        val database2 = object: Database1<String>() {}
        print(database2.tClass.canonicalName)
        /**
         * java.lang.String
         */
    
        val database3 = Database2.getInstance<String>()
        println(database3.tClass.canonicalName)
        /**
         * Exception in thread "main" java.lang.ClassCastException:
         * sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.Class
         * at com.ra.Database2.<init>(Database2.kt:6)
         * at com.ra.Database2$Companion$getInstance$1.<init>(Database2.kt:10)
         * at com.ra.Database2$Companion.getInstance(Database2.kt:10)
         * at com.ra.MainKt.main(Main.kt:19)
         */
    }
    

    正如你所看到的,只有一种成功的方式——方式2。但一点也不漂亮。由于我想将其包装在一个静态函数中(就像方式3所做的那样),它再次失败。

    下面是我的问题:

    1. 为什么会出错 ClassCastException 发生
    2. 有没有更好的方法来解决这个问题?如果喜欢C#'s解决方案,那就太好了。
    3. 我知道问题的原因是JVM在编译期间擦除了泛型类型,所以我想知道(出于好奇)这种情况何时才能改变?或者Kotlin或其他JVM语言可能会记录泛型类型信息?如果有,请告诉我。我已经受够了字型擦除。。。
    1 回复  |  直到 8 年前
        1
  •  1
  •   Yuri Schimke    8 年前

    它可以使用具体化的内联函数

    package com.ba
    
    import kotlin.reflect.KClass
    
    abstract class IDatabase<T> { }
    
    open class Database2<T: Any>(val tClass: KClass<T>) : IDatabase<T>() {
      // just wrap a `object` to confort the user
      companion object {
        inline fun <reified T: Any> getInstance(): Database2<T> = Database2(T::class)
      }
    }
    
    fun main(args: Array<String>) {
      val databaseString = Database2.getInstance<String>()
      println(databaseString.tClass)
    
      val databaseInt = Database2.getInstance<Int>()
      println(databaseInt.tClass)
    }
    

    输出

    class kotlin.String
    class kotlin.Int