TL; DR: Ce qui est reified
bon pour
fun <T> myGenericFun(c: Class<T>)
Dans le corps d'une fonction générique comme myGenericFun
, vous ne pouvez pas accéder au type T
car il est uniquement disponible au moment de la compilation mais effacé au moment de l'exécution. Par conséquent, si vous souhaitez utiliser le type générique en tant que classe normale dans le corps de la fonction, vous devez transmettre explicitement la classe en tant que paramètre, comme indiqué dans myGenericFun
.
Si vous créez une inline
fonction avec un reified T
, le type de T
peut être consulté même au moment de l'exécution et vous n'avez donc pas besoin de passer le Class<T>
. Vous pouvez travailler avec T
comme si elle était une classe normale, par exemple , vous pouvez vérifier si une variable est une instance de T
, que vous pouvez facilement faire alors: myVar is T
.
Une telle inline
fonction avec reified
type T
ressemble à ceci:
inline fun <reified T> myGenericFun()
Comment reified
fonctionne
Vous ne pouvez utiliser reified
qu'en combinaison avec une inline
fonction . Une telle fonction oblige le compilateur à copier le bytecode de la fonction à chaque endroit où la fonction est utilisée (la fonction est "en ligne"). Lorsque vous appelez une fonction en ligne avec un type réifié, le compilateur connaît le type réel utilisé comme argument de type et modifie le bytecode généré pour utiliser directement la classe correspondante. Par conséquent, les appels comme myVar is T
deviennent myVar is String
(si l'argument type l'était String
) dans le bytecode et à l'exécution.
Exemple
Jetons un coup d'œil à un exemple qui montre à quel point cela reified
peut être utile . Nous voulons créer une fonction d'extension pour String
called toKotlinObject
qui tente de convertir une chaîne JSON en un objet Kotlin simple avec un type spécifié par le type générique de la fonction T
. Nous pouvons utiliser com.fasterxml.jackson.module.kotlin
pour cela et la première approche est la suivante:
a) Première approche sans type réifié
fun <T> String.toKotlinObject(): T {
val mapper = jacksonObjectMapper()
//does not compile!
return mapper.readValue(this, T::class.java)
}
La readValue
méthode prend un type qu'elle est censée analyser le JsonObject
to. Si nous essayons d'obtenir le Class
paramètre de type T
, le compilateur se plaint: "Impossible d'utiliser 'T' comme paramètre de type réifié. Utilisez une classe à la place."
b) Solution de contournement avec un Class
paramètre explicite
fun <T: Any> String.toKotlinObject(c: KClass<T>): T {
val mapper = jacksonObjectMapper()
return mapper.readValue(this, c.java)
}
Pour contourner le problème, vous pouvez faire de Class
of T
un paramètre de méthode, qui sera ensuite utilisé comme argument de readValue
. Cela fonctionne et est un modèle courant dans le code Java générique. Il peut être appelé comme suit:
data class MyJsonType(val name: String)
val json = """{"name":"example"}"""
json.toKotlinObject(MyJsonType::class)
c) La voie Kotlin: reified
L'utilisation d'une inline
fonction avec reified
paramètre de type T
permet d'implémenter la fonction différemment:
inline fun <reified T: Any> String.toKotlinObject(): T {
val mapper = jacksonObjectMapper()
return mapper.readValue(this, T::class.java)
}
Il n'est pas nécessaire de prendre le Class
de en T
plus, T
peut être utilisé comme s'il s'agissait d'une classe ordinaire. Pour le client, le code ressemble à ceci:
json.toKotlinObject<MyJsonType>()
Remarque importante: utilisation de Java
Une fonction inline avec reified
type ne peut pas être appelée à partir du code Java .