如何在 Kotlin 中获取泛型参数 class

How to get generic parameter class in Kotlin

Firebase 的 snapshot.getValue() 预计调用如下:

snapshot?.getValue(Person::class.java)

但是我想用通过 class 声明传递到 class 的通用参数替换 Person,即

class DataQuery<T : IModel> 

并使用该通用参数执行如下操作:

snapshot?.getValue(T::class.java)

但是当我尝试这样做时,我收到一条错误消息,指出

only classes can be used on the left-hand side of a class literal

是否可以像在 C# 中那样对泛型参数提供 class 约束,或者是否有一些其他语法可用于获取泛型参数的类型信息?

您需要的是泛型参数的具体化修饰符,您可以在此处阅读相关内容。 https://kotlinlang.org/docs/reference/inline-functions.html#reified-type-parameters 所以如果你这样做:

inline fun <reified T : Any>T.logTag() = T::class.java.simpleName

您将获得实际呼叫者的姓名 class,而不是 "Object"。

对于带有通用参数 T 的 class,您不能这样做,因为您没有 T 的类型信息,因为 JVM 会删除类型信息。因此这样的代码无法工作:

class Storage<T: Any> {
    val snapshot: Snapshot? = ...

    fun retrieveSomething(): T? {
        return snapshot?.getValue(T::class.java) // ERROR "only classes can be used..."
    }
}

但是,如果 T 的类型被具体化并在内联函数中使用,您可以使这项工作有效:

class Storage {
    val snapshot: Snapshot? = ...

    inline fun <reified T: Any> retrieveSomething(): T? {
        return snapshot?.getValue(T::class.java)
    }
}

请注意,内联函数 if public 只能访问 class 的 public 个成员。但是您可以有两个函数变体,一个接收非内联的 class 参数并访问私有内部结构,另一个内联辅助函数从推断类型参数进行具体化:

class Storage {
    private val snapshot: Snapshot? = ...

    fun <T: Any> retrieveSomething(ofClass: Class<T>): T? {
        return snapshot?.getValue(ofClass)
    }
    
    inline fun <reified T: Any> retrieveSomething(): T? {
        return retrieveSomething(T::class.java)
    }
}

您还可以使用 KClass 而不是 Class,这样仅使用 Kotlin 的调用方就可以使用 MyClass::class 而不是 MyClass::class.java

如果想让class配合泛型的内联方法(也就是说classStorage只存储T类型的对象):

class Storage <T: Any> {
    val snapshot: Snapshot? = ...

    inline fun <reified R: T> retrieveSomething(): R? {
        return snapshot?.getValue(R::class.java)
    }
}

内联函数中具体化类型的link:https://kotlinlang.org/docs/reference/inline-functions.html#reified-type-parameters

你可以像这样得到它的 class 类型

snapshot?.getValue((this.javaClass
                        .genericSuperclass as ParameterizedType)
                        .actualTypeArguments[0] as Class<T>)
  1. 使用class比较。参见

     inline fun <reified T> SharedPreferences.getData(key: String, defValue: Any? = null): T? =
         when (T::class) {
             Integer::class -> {
                 val value = getInt(key, (defValue as? Int) ?: 0) as T
                 if (value == 0) defValue as? T else value
             }
             Long::class -> {
                 val value = getLong(key, (defValue as? Long) ?: 0L) as T
                 if (value == 0) defValue as? T else value
             }
             Boolean::class -> {
                 val value = getBoolean(key, (defValue as? Boolean) ?: false) as T
                 if (value == false) defValue as? T else value
             }
             String::class -> getString(key, defValue as? String) as T?
             else -> throw IllegalStateException("Unsupported type")
         }
    
  2. 使用 isAssignableFrom。参见 https://dev.to/cjbrooks12/kotlin-reified-generics-explained-3mie

     inline fun <reified T : Number> SharedPreferences.getData(key: String): T? {
         val cls = T::class.java
         return if (cls.isAssignableFrom(Integer::class.java)) {
             getInt(key, 0) as T
         } else if (cls.isAssignableFrom(Long::class.java)) {
             getLong(key, 0) as T
         } else {
             throw IllegalStateException("Unsupported type")
         }
     }
    

对于 SharedPreferences 中的 Double,请参阅 。

使用:

val s: String? = preferences.getData("key", "")
val i = preferences.getData<Int>("key")

使用typeOf 是另一种选择。 (在 Kotlin 1.3 中添加)

示例:

@OptIn(ExperimentalStdlibApi::class)
inline fun <reified T> someMethod(data: T) {

    val typeClassifier = typeOf<T>().classifier

    if (typeClassifier == List::class) {
        //Do something
    }

}

这就是我的。

class ClubsViewModel<T>(clazz: Class<T>) :  BaseViewModel<T>(clazz) {
    private var _mClubs = MutableLiveData<List<T>>()

     listenToFireStoreCollection("Clubs", _mClubs)
...
}

class BViewModel<T>(clazz: Class<T>) :  BaseViewModel<T>(clazz) { 
  
    private var _mBs = MutableLiveData<List<T>>()
    listenToFireStoreCollection("Bname", _mBs)
...
}

class BaseViewModel<T>(val clazz: Class<T>) {
    protected val mFirestore = Firebase.firestore

    protected  fun listenToFireStoreCollection(val collectionName: String, liveData: MutableLiveData<List<T>>) 
        mFirestore.collection(collectionName).addSnapshotListener { snapshot, e ->
            if (e != null) {
                return@addSnapshotListener
            }
            if (snapshot != null) {
                liveData.value = snapshot.documents.mapNotNull { it.toObject(clazz) }
            }
        }
    }
}
//FRAGMENT EXAMPLES.
class ClubsFragment : Fragment() {

    private val mClubsViewModel: ClubsViewModel<ClubsFSEntity> by viewModels()
...
}

class BsFragment : Fragment() {

    private val mBsViewModel: BsViewModel<BsFSEntity> by viewModels()
...
}

我在这里遇到了类似的问题:Hilt Dependency injection with Class in ViewModel