Firebase的snapshot.getValue()
预计会被调用如下:
snapshot?.getValue(Person::class.java)
但是我想用Person
替换通过类声明传递给类的泛型参数,即
class DataQuery<T : IModel>
并使用该泛型参数执行以下操作:
snapshot?.getValue(T::class.java)
但是当我尝试我收到错误声明
时只能在类文字的左侧使用类
是否可以像在C#中那样为泛型参数提供类约束,还是可以使用其他语法来获取泛型参数的类型信息?
答案 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