如何在Kotlin中获取泛型类型参数的类

Ric*_*rdo 4 generics android kotlin firebase-realtime-database

我想从泛型类型获取类属性T.我决定延伸到Any但是我收到了一个错误. https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-any/index.html#extension-properties

我有以下代码:

class FirebaseDBRepo<T : Any>(val child:String) {

private var callback: FirebaseDatabaseRepositoryCallback<T>? = null
private val ref: DatabaseReference
private val listener = object : ValueEventListener {
    override fun onDataChange(dataSnapshot: DataSnapshot) {

        //T::class.java is showing the error cannot use t as reified type parameter use class instead

        val gameDS = dataSnapshot.getValue(T::class.java)
        callback!!.onSuccess(gameDS!!)
    }

    override fun onCancelled(databaseError: DatabaseError) {

    }
}

init {
    ref = FirebaseDatabase.getInstance().reference.child(child)
}


fun addListener(callback: FirebaseDatabaseRepositoryCallback<T>) {
    this.callback = callback
    ref.addValueEventListener(listener)
}

fun removeListener() {
    ref.removeEventListener(listener)
}

}
Run Code Online (Sandbox Code Playgroud)

Zoe*_*Zoe 12

您只能在已知的变量上获取该类.在java中也发生了同样的事情,但是消息略有不同:

public <T> void x(){
    T t = T.class.newInstance();
}
Run Code Online (Sandbox Code Playgroud)

在Java中,你可以解决这个问题:

public <T> void x(Class<T> cls){
    T t = cls.newInstance();
}
Run Code Online (Sandbox Code Playgroud)

这同样适用于Kotlin和任何电话.在大多数情况下,您需要获取类实例.但是,Kotlin使用关键字支持reified泛型,但仅支持内联泛型函数.你可以传递一个类,但在函数中,只需使用reified关键字就可以了.

因为你不能声明一个具有reified泛型的类,这意味着这是无效的:

class SomeClass<reified T>
Run Code Online (Sandbox Code Playgroud)

但它对内联函数有效,这意味着你可以这样做:

inline fun <reified T> someFunction()
Run Code Online (Sandbox Code Playgroud)

所以你有两个选择.但是,由于您扩展了一个监听器,因此将该泛型添加到该函数的第一个选项不是一个选项.您不能使用泛型覆盖非泛型方法.它不会编译.

这留下了第二种选择,不幸的是,这种选择相当苛刻; 将类传递给构造函数.所以看起来应该是这样的:

class FirebaseDBRepo<T : Any>(val child: String, private val cls: Class<T>) {
Run Code Online (Sandbox Code Playgroud)

现在,我不使用Firebase,所以我不知道你要传递哪些类,所以对于下一个例子,我只是使用String.

Kotlin支持某种类型的最小化,而不需要转换为原始类型.这个:

val t = FirebaseDBRepo<String>("", String::class.java)
Run Code Online (Sandbox Code Playgroud)

可以缩短到这个:

val t = FirebaseDBRepo("", String::class.java)
Run Code Online (Sandbox Code Playgroud)

两种情况下的推断类型都是FirebaseDBRepo<String>.


小智 8

由于您在 JVM 上运行,因此类型擦除是一回事。这意味着(简单来说),在编译期间,泛型会被简单地忽略。因此,您无法获得 T 的类,因为 JVM 甚至不知道您所说的“T”是什么意思。

在某些情况下,Kotlin 使用一个聪明的技巧来解决这个限制。当您使用内联函数时,编译器不会调用您定义的函数,而是将整个函数体复制到您调用它的位置。这只能用于内联函数。不是上课。

有一个很困难的解决方法:只需添加 private val classT: Class<T> 到构造函数并改用参数!