Storia della domanda:
Nella JVM, durante l'esecuzione, non è presente alcuna informazione sui parametri di generics (type erasure). Kotlin ha proposto un meccanismo per i parametri generics reified per le funzioni inline, per implementare l'accesso alle informazioni sul tipo T durante l'esecuzione senza ricorrere a trucchi come passare Class<T>. Questo è uno degli strumenti più potenti del linguaggio Kotlin.
Problema:
È necessario scrivere una funzione che accetta un certo valore di tipo T (generic) e, a seconda del tipo del parametro, esegue azioni diverse, senza passare esplicitamente java.lang.Class e senza riflessione in stile Java. Il classico type erasure non consente di conoscere il tipo T durante l'esecuzione.
Soluzione:
In Kotlin, per le funzioni inline, è possibile dichiarare parametri generics con il modificatore reified, il che consente di "catturare" il tipo T all'interno del corpo della funzione, lavorare con esso come con un tipo normale, effettuare type-checks e creare istanze tramite riflessione.
Esempio di codice:
inline fun <reified T> isOfType(value: Any): Boolean { return value is T } isOfType<String>(123) // false isOfType<Int>(123) // true
Caratteristiche chiave:
È possibile utilizzare reified al di fuori delle funzioni inline?
No! Solo le funzioni inline (e le proprietà inline lazy) possono avere parametri generics reified. Il motivo è che la sostituzione del codice avviene al momento della chiamata, solo in questo modo è possibile "sostituire" un tipo specifico al posto di T.
Esempio di codice:
// Errore di compilazione: fun <reified T> errorFun() { } // Variante corretta: inline fun <reified T> okFun() { }
È possibile ottenere Class<T> all'interno di una funzione inline reified?
Sì! Per fare ciò è sufficiente scrivere T::class.java oppure T::class. Questo è estremamente utile per scrivere fabbriche generiche, parser e lavorare con l'API di riflessione.
Esempio di codice:
inline fun <reified T> printType() { println(T::class.java) } printType<String>() // class java.lang.String
È possibile creare istanze del tipo T tramite costruttore in una funzione reified?
Parzialmente. È possibile utilizzare la riflessione, ma non è possibile fare direttamente new T() come in C++ o C#:
inline fun <reified T : Any> makeInstance(): T? { return T::class.constructors.firstOrNull()?.call() }
Ma questo approccio richiede la presenza di un costruttore senza parametri per T.
Funzione con reified che chiama un'operazione di riflessione pesante:
inline fun <reified T> foo(): T? = T::class.constructors.firstOrNull()?.call()
Pro:
Contro:
Utilizzo di reified per un controllo di tipo universale o safeCast:
inline fun <reified T> safeCast(value: Any?): T? = value as? T
Pro:
Contro: