TL; DR: Che cosa è reified
bene per
fun <T> myGenericFun(c: Class<T>)
Nel corpo di una funzione generica come myGenericFun
, non è possibile accedere al tipo T
perché è disponibile solo in fase di compilazione ma cancellato in fase di esecuzione. Pertanto, se si desidera utilizzare il tipo generico come classe normale nel corpo della funzione, è necessario passare esplicitamente la classe come parametro, come mostrato in myGenericFun
.
Se si crea una inline
funzione con un reified T
, tuttavia, è T
possibile accedere al tipo di anche in fase di esecuzione e quindi non è necessario passarlo Class<T>
ulteriormente. È possibile lavorare con T
come se fosse una classe normale, ad esempio, si potrebbe voler controllare se una variabile è un esempio di T
che si può facilmente fare poi: myVar is T
.
Tale inline
funzione con reified
tipo ha T
il seguente aspetto:
inline fun <reified T> myGenericFun()
Come reified
funziona
È possibile utilizzare solo reified
in combinazione con una inline
funzione . Tale funzione fa sì che il compilatore copi il bytecode della funzione in ogni luogo in cui la funzione viene utilizzata (la funzione viene "incorporata"). Quando si chiama una funzione inline con tipo reificato, il compilatore conosce il tipo effettivo utilizzato come argomento di tipo e modifica il bytecode generato per utilizzare direttamente la classe corrispondente. Pertanto chiamate come myVar is T
diventano myVar is String
(se l'argomento tipo fosse String
) nel bytecode e in fase di esecuzione.
Esempio
Diamo un'occhiata a un esempio che mostra quanto reified
possa essere utile . Vogliamo creare una funzione di estensione per la String
chiamata toKotlinObject
che tenti di convertire una stringa JSON in un semplice oggetto Kotlin con un tipo specificato dal tipo generico della funzione T
. Possiamo usare com.fasterxml.jackson.module.kotlin
per questo e il primo approccio è il seguente:
a) Primo approccio senza tipo reificato
fun <T> String.toKotlinObject(): T {
val mapper = jacksonObjectMapper()
//does not compile!
return mapper.readValue(this, T::class.java)
}
Il readValue
metodo prende un tipo che dovrebbe analizzare il JsonObject
. Se proviamo a ottenere il Class
parametro del tipo T
, il compilatore si lamenta: "Impossibile utilizzare 'T' come parametro del tipo reificato. Usa invece una classe."
b) Soluzione alternativa con Class
parametro esplicito
fun <T: Any> String.toKotlinObject(c: KClass<T>): T {
val mapper = jacksonObjectMapper()
return mapper.readValue(this, c.java)
}
Per ovviare al problema, Class
of T
può essere impostato come parametro del metodo, che viene quindi utilizzato come argomento per readValue
. Funziona ed è un modello comune nel codice Java generico. Può essere chiamato come segue:
data class MyJsonType(val name: String)
val json = """{"name":"example"}"""
json.toKotlinObject(MyJsonType::class)
c) La via di Kotlin: reified
L'uso di una inline
funzione con reified
parametro type consente T
di implementare la funzione in modo diverso:
inline fun <reified T: Any> String.toKotlinObject(): T {
val mapper = jacksonObjectMapper()
return mapper.readValue(this, T::class.java)
}
Non c'è bisogno di prendere il Class
di in T
più, T
può essere usato come se fosse una classe normale. Per il client il codice è simile al seguente:
json.toKotlinObject<MyJsonType>()
Nota importante: lavorare con Java
Una funzione incorporata con reified
tipo non è richiamabile dal codice Java .