snapshot.getValue()
de Firebase s'attend à être appelé comme suit:
snapshot?.getValue(Person::class.Java)
Cependant, je voudrais remplacer Person
par un paramètre générique qui est transmis à la classe via la déclaration de classe i.e.
class DataQuery<T : IModel>
et utilisez ce paramètre générique pour faire quelque chose comme ceci:
snapshot?.getValue(T::class.Java)
mais quand je tente d'obtenir une erreur indiquant que
seules les classes peuvent être utilisées à gauche d'un littéral de classe
Est-il possible de fournir une contrainte de classe sur le paramètre générique comme en C # ou existe-t-il une autre syntaxe que je peux utiliser pour obtenir les informations de type du paramètre générique?
Pour une classe avec le paramètre générique T, vous ne pouvez pas le faire car vous ne disposez d'aucune information de type pour T car la machine virtuelle Java efface les informations de type. Par conséquent, un code comme celui-ci ne peut pas fonctionner:
class Storage<T: Any> {
val snapshot: Snapshot? = ...
fun retrieveSomething(): T? {
return snapshot?.getValue(T::class.Java) // ERROR "only classes can be used..."
}
}
Mais vous pouvez faire ce travail si le type de T est réifié et utilisé dans une fonction inline:
class Storage {
val snapshot: Snapshot? = ...
inline fun <reified T: Any> retrieveSomething(): T? {
return snapshot?.getValue(T::class.Java)
}
}
Notez que la fonction inline si public ne peut accéder qu'aux membres publics de la classe. Mais vous pouvez avoir deux variantes de la fonction, une qui reçoit un paramètre de classe qui n'est pas en ligne et accède aux internes privés, et une autre fonction d'assistance en ligne qui effectue la réification à partir du paramètre de type inféré:
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)
}
}
Vous pouvez également utiliser KClass
au lieu de Class
pour que les appelants qui utilisent uniquement Kotlin puissent simplement utiliser MyClass::class
au lieu de MyClass::class.Java
.
Si vous souhaitez que la classe coopère avec la méthode inline sur les génériques (ce qui signifie que la classe Storage
ne stocke que les objets de type T
):
class Storage <T: Any> {
val snapshot: Snapshot? = ...
inline fun <reified R: T> retrieveSomething(): R? {
return snapshot?.getValue(R::class.Java)
}
}
Le lien vers les types réifiés dans les fonctions en ligne: https://kotlinlang.org/docs/reference/inline-functions.html#reified-type-parameters
Ce dont vous avez besoin est un modificateur reified pour votre paramètre générique, vous pouvez en savoir plus ici . https://kotlinlang.org/docs/reference/inline-functions.html#reified-type-parameters . Alors, si vous faites quelque chose comme ça:
inline fun <reified T : Any>T.logTag() = T::class.Java.simpleName
vous obtiendrez le nom de la classe de l'appelant, et non "Object".
vous pouvez obtenir son type de classe comme celui-ci
snapshot?.getValue((this.javaClass
.genericSuperclass as ParameterizedType)
.actualTypeArguments[0] as Class<T>)