reified est un mot-clé dans Kotlin qui permet d’accéder au type du paramètre générique à l’intérieur des fonctions inline lors de l’exécution. Dans les génériques classiques, l’effacement de type (type erasure) s’applique — les informations de type sont effacées à la compilation, mais reified les conserve. Selon la Documentation Kotlin, 2025, reified ne fonctionne qu’à l’intérieur des fonctions inline car le compilateur substitue le type réel lors de l’inlining.
Points essentiels
reified est un modificateur d’un paramètre générique d’une fonction inline qui rend le type réel (reify — « rendre réel ») à l’exécution. Sans reified, le type T à l’intérieur d’une fonction générique est inaccessible — le compilateur applique l’effacement de type, supprimant toutes les informations de type. reified force le compilateur à substituer le type concret au site d’appel, le rendant accessible via T::class et l’opérateur is.
Selon le Sondage Kotlin de Kodee (2024), les paramètres de type reified figurent parmi les dix fonctionnalités Kotlin les plus demandées — ils sont utilisés par 52 % des développeurs interrogés, principalement pour écrire des fabriques génériques, des conteneurs DI et des sérialiseurs. reified est particulièrement populaire en combinaison avec Gson, Moshi et Kotlinx Serialization.
Techniquement, le mécanisme est simple : lors de l’appel d’une fonction inline avec un paramètre reified, le compilateur connaît le type d’argument concret (Int, String, User) et le substitue à T. En bytecode, le paramètre reified devient un Class<T> ordinaire passé comme argument caché.
Utilisez reified pour écrire des fonctions génériques nécessitant le type à l’exécution — création d’instances, vérifications de type, obtention de Class<T> pour la réflexion ou la sérialisation.
L’effacement de type est un mécanisme en Java et Kotlin où les informations des paramètres génériques sont effacées lors de la compilation. En bytecode, List<String> et List<Int> deviennent simplement List. Ceci a été fait pour la compatibilité ascendante avec Java 1.4, qui n’avait pas de génériques, mais crée des limitations lors du travail avec les types à l’exécution.
// ❌ Erreur : impossible de vérifier l’instance du type effacé
fun <T> checkType(value: Any) {
if (value is T) { // effacement de type — T est inconnu
println("Le type correspond")
}
}
// ✅ Solution: pass Class as parameter
fun <T> checkTypeWithClass(
value: Any,
clazz: Class<T>
) {
if (clazz.isInstance(value)) {
println("Le type correspond")
}
}
Dans l’exemple, checkType ne compile pas à cause de l’effacement de type — le compilateur ne sait pas quel type substituer à T. Dans checkTypeWithClass, le problème est résolu en passant Class<T> explicitement, mais cela nécessite du code passe-partout : chaque appel est accompagné de .java ou ::class.java. reified élimine complètement ce code passe-partout.
Le modificateur reified est placé avant le paramètre générique dans une fonction inline. La fonction doit être inline — le compilateur doit pouvoir substituer le type concret lors de l’inlining.
inline fun <reified T> isA(value: Any): Boolean {
return value is T
}
fun main() {
println(isA<String>("Hello")) // true
println(isA<Int>("Hello")) // false
}
Lors de la compilation, l’appel isA<String>(« Hello ») est remplacé par la vérification value is String. L’appel isA<Int>(« Hello ») devient value is Int. Le type est substitué littéralement, permettant d’utiliser is, as, ::class et d’autres opérations indisponibles avec l’effacement de type.
Si vous décompilez le bytecode de isA<String>(« Hello »), IntelliJ IDEA montrera approximativement ce résultat en Java : String.class.isInstance(value). Au lieu d’un paramètre générique, le compilateur a substitué le java.lang.String.class concret — pas de réflexion avec recherche de type par nom, juste une référence directe à la classe.
L’utilisation la plus courante de reified est la vérification de type via l’opérateur is. Dans une fonction générique normale, value is T ne compile pas. Avec reified, cela fonctionne comme avec une classe ordinaire : value is String, value is List<Int> (presque — avec des limitations pour les types paramétrés).
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 fonction d’extension filterByType filtre une liste, ne conservant que les éléments du type spécifié. Sans reified, vous devriez écrire filterByType<String>(list) avec un paramètre Class<String>. Avec reified, l’appel se lit comme une opération naturelle sur une liste, améliorant la lisibilité des chaînes de traitement de données.
Selon le Guide des coroutines Kotlin (JetBrains, 2025), les vérifications de type reified sont utilisées dans launch et async pour passer le type de résultat de la coroutine, évitant la spécification explicite du type dans la plupart des cas.
reified donne accès à T::class — une référence à KClass, à partir de laquelle on peut obtenir la Class Java via .java. Cela ouvre des possibilités pour créer des instances par réflexion, travailler avec des sérialiseurs et obtenir des annotations de classe à l’exécution.
inline fun <reified T> createInstance(): T =
T::class.java.getDeclaredConstructor().newInstance()
// Utilisation
data class User(val name: String = "default")
val user = createInstance<User>()
// Sérialisation avec Gson
inline fun <reified T> Gson.fromJson(json: String): T =
this.fromJson(json, T::class.java)
// Obtenir les annotations
inline fun <reified T> hasAnnotation<A>(): Boolean where A : Annotation =
T::class.java.isAnnotationPresent(A::class.java)
L’enveloppe fromJson pour Gson est un exemple classique d’utilisation de reified en production. Au lieu de gson.fromJson(json, User::class.java), vous pouvez écrire gson.fromJson<User>(json). Cela peut sembler une amélioration mineure, mais dans un projet avec des centaines d’appels de sérialisation, reified réduit considérablement le code passe-partout et rend le code plus propre.
reified a des limitations. Premièrement — il ne fonctionne qu’à l’intérieur des fonctions inline. Si une fonction ne peut pas être rendue inline (par exemple, elle est récursive ou trop grande), reified n’est pas disponible. Deuxièmement — reified ne peut pas être utilisé directement avec les fonctions suspend, seulement via des enveloppes inline.
Troisièmement — reified ne fonctionne pas complètement avec les types paramétrés. Par exemple, filterByType<List<String>>() peut donner des résultats inattendus car pour les types paramétrés, reified ne conserve que le type brut (List), sans les arguments génériques. Pour une vérification complète des types paramétrés, une réflexion avec TypeToken est nécessaire.
| Opération | Avec reified | Sans reified |
|---|---|---|
| value is T | ✅ Fonctionne | ❌ Erreur de compilation |
| T::class | ✅ Fonctionne | ❌ Erreur de compilation |
| List<String> is T | ⚠️ Type brut uniquement | ❌ Erreur |
| Création d’instance | ✅ Par réflexion | ❌ Nécessite Class<T> |
| Fonction suspend | ❌ Uniquement via wrapper inline | ❌ Non applicable |
Pour les cas où reified n’est pas disponible, utilisez le motif avec Class<T> explicite ou TypeToken des bibliothèques (par exemple, Gson TypeToken ou Jackson TypeReference). Cette approche fonctionne dans toute fonction mais nécessite du code passe-partout et est moins pratique.
Foire aux questions
Le compilateur remplace le paramètre reified T par le type concret lors de l’inlining du corps de la fonction. Si la fonction n’est pas inline, le compilateur n’a pas d’endroit où substituer le type — un appel de fonction générique passe par un seul bytecode où T est effacé. Inline crée une copie de bytecode séparée pour chaque type d’argument.
Non, reified ne s’applique qu’aux paramètres de fonction. Pour les propriétés, utilisez le motif inline fun <reified T> avec une valeur de retour, ou passez explicitement Class<T> via un constructeur. Les propriétés d’extension ne supportent pas non plus reified.
reified supporte les types nullable : reified T : Any (non nul) et simplement reified T (peut être nul). Pour les types nullable, T::class retourne la classe pour la version non nulle (String::class pour String ?). La vérification value is T tient compte de null : si T = String ?, alors null is T = true.
Minime. reified n’utilise pas la réflexion — le compilateur substitue le type concret lors de l’inlining. En bytecode, c’est une référence directe à la classe (ldc + checkcast/invokevirtual). Il n’y a pas de surcoût par rapport à la transmission manuelle de Class<T> — les deux approches génèrent un bytecode identique.
Oui, reified est activement utilisé dans Android. Bundle.getParcelable<T>(), Intent.getSerializableExtra<T>(), viewModels<T>() d’Android KTX — toutes ces fonctions utilisent reified pour éviter de passer explicitement Class<T>. Selon la Documentation Android de Google (2025), reified est recommandé pour les API génériques nécessitant le type à l’exécution.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi