如何在Kotlin

时间:2015-12-06 20:40:04

标签: kotlin

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

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

但是我想用Person替换通过类声明传递给类的泛型参数,即

class DataQuery<T : IModel> 

并使用该泛型参数执行以下操作:

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

但是当我尝试我收到错误声明

  

只能在类文字的左侧使用类

是否可以像在C#中那样为泛型参数提供类约束,还是可以使用其他语法来获取泛型参数的类型信息?

6 个答案:

答案 0 :(得分:36)

对于具有泛型参数T的类,您不能这样做,因为您没有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)
    }
}

请注意,内联函数如果public只能访问该类的公共成员。但是你可以有两个函数变体,一个接收一个非内联的类参数并访问私有内部,另一个内联辅助函数从推断的类型参数中进行实现:

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

如果您希望该类与泛型上的内联方法配合使用(意味着类Storage仅存储T类型的对象):

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

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

内联函数中已知类型的链接:https://kotlinlang.org/docs/reference/inline-functions.html#reified-type-parameters

答案 1 :(得分:18)

你需要的是你的通用参数的reified修饰符,你可以在这里阅读它。 https://kotlinlang.org/docs/reference/inline-functions.html#reified-type-parameters 所以,如果你这样做:

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

您将获得实际来电者课程的名称,而不是&#34;对象&#34;。

答案 2 :(得分:3)

您可以像这样获得其类类型

snapshot?.getValue((this.javaClass
                        .genericSuperclass as ParameterizedType)
                        .actualTypeArguments[0] as Class<T>)

答案 3 :(得分:0)

请参见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,请参见https://stackoverflow.com/a/45412036/2914140

使用:

val i = preferences.getData<Int>("key")

答案 4 :(得分:0)

使用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
    }

}

答案 5 :(得分:0)

这就是我所拥有的。

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<T> in ViewModel