reified è una parola chiave in Kotlin che consente di accedere al tipo del parametro generico all'interno delle funzioni inline in fase di esecuzione. Nei generics normali si applica l'eliminazione del tipo (type erasure) — le informazioni sul tipo vengono cancellate in fase di compilazione, ma reified le preserva. Secondo la Documentazione Kotlin, 2025, reified funziona solo all'interno di funzioni inline perché il compilatore sostituisce il tipo reale in fase di inline.
Punti chiave
reified è un modificatore di un parametro generico di una funzione inline che rende il tipo reale (reify — “rendere reale”) in fase di esecuzione. Senza reified, il tipo T all'interno di una funzione generica è inaccessibile — il compilatore applica l'eliminazione del tipo, rimuovendo tutte le informazioni sul tipo. reified costringe il compilatore a sostituire il tipo concreto nel sito di chiamata, rendendolo accessibile tramite T::class e l'operatore is.
Secondo il Sondaggio Kotlin di Kodee (2024), i parametri di tipo reified sono tra le dieci funzionalità Kotlin più richieste — sono utilizzati dal 52% degli sviluppatori intervistati, principalmente per scrivere fabbriche generiche, contenitori DI e serializzatori. reified è particolarmente popolare in combinazione con Gson, Moshi e Kotlinx Serialization.
Tecnicamente, il meccanismo è semplice: quando si chiama una funzione inline con un parametro reified, il compilatore conosce il tipo di argomento concreto (Int, String, User) e lo sostituisce a T. In bytecode, il parametro reified diventa un normale Class<T> passato come argomento nascosto.
Utilizza reified per scrivere funzioni generiche dove il tipo è necessario in fase di esecuzione — creazione di istanze, controlli di tipo, ottenimento di Class<T> per riflessione o serializzazione.
La cancellazione del tipo (type erasure) è un meccanismo in Java e Kotlin dove le informazioni dei parametri generici vengono cancellate durante la compilazione. In bytecode, List<String> e List<Int> diventano semplicemente List. Questo è stato fatto per compatibilità con Java 1.4, che non aveva generics, ma crea limitazioni quando si lavora con i tipi in fase di esecuzione.
// ❌ Errore: impossibile controllare l'istanza del tipo cancellato
fun <T> checkType(value: Any) {
if (value is T) { // cancellazione del tipo — T è sconosciuto
println("Il tipo corrisponde")
}
}
// ✅ Solution: pass Class as parameter
fun <T> checkTypeWithClass(
value: Any,
clazz: Class<T>
) {
if (clazz.isInstance(value)) {
println("Il tipo corrisponde")
}
}
Nell'esempio, checkType non viene compilato a causa della cancellazione del tipo — il compilatore non sa quale tipo sostituire a T. In checkTypeWithClass, il problema è risolto passando Class<T> esplicitamente, ma questo richiede boilerplate: ogni chiamata è accompagnata da .java o ::class.java. reified elimina completamente questo boilerplate.
Il modificatore reified viene posto prima del parametro generico in una funzione inline. La funzione deve essere inline — il compilatore deve essere in grado di sostituire il tipo concreto in fase di inline.
inline fun <reified T> isA(value: Any): Boolean {
return value is T
}
fun main() {
println(isA<String>("Hello")) // true
println(isA<Int>("Hello")) // false
}
Durante la compilazione, la chiamata isA<String>(“Hello”) viene sostituita dal controllo value is String. La chiamata isA<Int>(“Hello”) diventa value is Int. Il tipo viene sostituito letteralmente, consentendo l'uso di is, as, ::class e altre operazioni non disponibili con la cancellazione del tipo.
Se si decompila il bytecode di isA<String>(“Hello”), IntelliJ IDEA mostrerà approssimativamente questo risultato in Java: String.class.isInstance(value). Invece di un parametro generico, il compilatore ha sostituito il concreto java.lang.String.class — nessuna riflessione con ricerca del tipo per nome, solo un riferimento diretto alla classe.
L'uso più comune di reified è il controllo del tipo tramite l'operatore is. In una funzione generica normale, value is T non viene compilato. Con reified, funziona come con una classe normale: value is String, value is List<Int> (quasi — con limitazioni per i tipi parametrizzati).
inline fun <reified T> List<Any>.filterByType(): List<T> {
return this.filter { it is T }.map { it as T }
}
val mixed = listOf("a", 1, "b", 2)
val strings = mixed.filterByType<String>() // ["a", "b"]
val ints = mixed.filterByType<Int>() // [1, 2]
La funzione di estensione filterByType filtra una lista, mantenendo solo gli elementi del tipo specificato. Senza reified, dovresti scrivere filterByType<String>(list) con un parametro Class<String>. Con reified, la chiamata si legge come un'operazione naturale su una lista, migliorando la leggibilità delle catene di elaborazione dati.
Secondo la Guida alle Coroutine Kotlin (JetBrains, 2025), i controlli di tipo reified vengono utilizzati in launch e async per passare il tipo di risultato della coroutine, evitando la specifica esplicita del tipo nella maggior parte dei casi.
reified fornisce accesso a T::class — un riferimento a KClass, da cui è possibile ottenere la Class Java tramite .java. Questo apre possibilità per creare istanze tramite riflessione, lavorare con serializzatori e ottenere annotazioni di classe in fase di esecuzione.
inline fun <reified T> createInstance(): T =
T::class.java.getDeclaredConstructor().newInstance()
// Utilizzo
data class User(val name: String = "default")
val user = createInstance<User>()
// Serializzazione con Gson
inline fun <reified T> Gson.fromJson(json: String): T =
this.fromJson(json, T::class.java)
// Ottenere annotazioni
inline fun <reified T> hasAnnotation<A>(): Boolean where A : Annotation =
T::class.java.isAnnotationPresent(A::class.java)
Il wrapper fromJson per Gson è un classico esempio di utilizzo di reified in produzione. Invece di gson.fromJson(json, User::class.java), puoi scrivere gson.fromJson<User>(json). Può sembrare un miglioramento minore, ma in un progetto con centinaia di chiamate di serializzazione, reified riduce significativamente il boilerplate e rende il codice più pulito.
reified ha delle limitazioni. Primo — funziona solo all'interno di funzioni inline. Se una funzione non può essere resa inline (ad esempio, è ricorsiva o troppo grande), reified non è disponibile. Secondo — reified non può essere usato direttamente con funzioni suspend, solo tramite wrapper inline.
Terzo — reified non funziona completamente con i tipi parametrizzati. Ad esempio, filterByType<List<String>>() può dare risultati inaspettati perché per i tipi parametrizzati, reified preserva solo il tipo raw (List), senza gli argomenti generici. Per il controllo completo dei tipi parametrizzati, è necessaria la riflessione con TypeToken.
| Operazione | Con reified | Senza reified |
|---|---|---|
| value is T | ✅ Funziona | ❌ Errore di compilazione |
| T::class | ✅ Funziona | ❌ Errore di compilazione |
| List<String> is T | ⚠️ Solo tipo raw | ❌ Errore |
| Creazione di istanza | ✅ Tramite riflessione | ❌ Necessita Class<T> |
| Funzione suspend | ❌ Solo tramite wrapper inline | ❌ Non applicabile |
Per i casi in cui reified non è disponibile, usa il pattern con Class<T> esplicito o TypeToken dalle librerie (ad esempio, Gson TypeToken o Jackson TypeReference). Questo approccio funziona in qualsiasi funzione ma richiede boilerplate ed è meno comodo.
Domande frequenti
Il compilatore sostituisce il parametro reified T con il tipo concreto durante l'inline del corpo della funzione. Se la funzione non è inline, il compilatore non ha un posto dove sostituire il tipo — una chiamata di funzione generica passa attraverso un unico bytecode dove T è cancellato. Inline crea una copia separata del bytecode per ogni tipo di argomento.
No, reified si applica solo ai parametri di funzione. Per le proprietà, usa il pattern inline fun <reified T> con un valore di ritorno, o passa esplicitamente Class<T> tramite un costruttore. Anche le proprietà di estensione non supportano reified.
reified supporta i tipi nullable: reified T : Any (non nullo) e semplicemente reified T (può essere nullo). Per i tipi nullable, T::class restituisce la classe per la versione non nulla (String::class per String?). Il controllo value is T tiene conto di null: se T = String?, allora null is T = true.
Minimo. reified non usa riflessione — il compilatore sostituisce il tipo concreto in fase di inline. In bytecode, questo è un riferimento diretto alla classe (ldc + checkcast/invokevirtual). Non c'è overhead rispetto al passaggio manuale di Class<T> — entrambi gli approcci generano bytecode identico.
Sì, reified è attivamente utilizzato in Android. Bundle.getParcelable<T>(), Intent.getSerializableExtra<T>(), viewModels<T>() da Android KTX — tutte queste funzioni usano reified per evitare di passare esplicitamente Class<T>. Secondo la Documentazione Android di Google (2025), reified è raccomandato per API generiche dove il tipo è necessario in fase di esecuzione.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche