ProgrammationDéveloppeur Backend

Comment les generics sont-ils implémentés en Kotlin ? Quelles sont les limitations, comment fonctionnent l'invariance, la covariance et la contravariance, et en quoi diffèrent-elles des génériques en Java ? Donnez des exemples d'utilisation.

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse

Generics en Kotlin permettent de créer des structures de données et des fonctions universelles et sûres au niveau des types. La principale caractéristique : Kotlin implémente le système de types génériques au niveau de la compilation, tout comme Java, mais avec une typisation plus stricte et une syntaxe de variance (covariance et contravariance) plus étendue.

Limitations des génériques :

  • Les paramètres de type sont invariants par défaut.
  • Il n'est pas possible de créer une instance d'un paramètre de type (T() est interdit).
  • Pas d'accès aux informations sur les types génériques pendant l'exécution (effacement de type).

Variance :

  • Covariance (out T) : permet d'utiliser des sous-types.
  • Contravariance (in T) : permet d'utiliser des super-types.
  • Invariance : type sans modificateur de variance.

Exemple de covariance :

interface Producer<out T> { fun produce(): T }

Exemple de contravariance :

interface Consumer<in T> { fun consume(item: T) }

Différences avec Java :

  • La syntaxe est plus explicite et concise (out au lieu de ? extends, in au lieu de ? super).
  • Pas de types génériques (?, seulement in/out).
  • Interdiction de créer une instance d'un paramètre générique.

Question piège

Question : "Peut-on déclarer un tableau de tableaux (Array<Array<Int>>) comme Array<out Array<Int>> en Kotlin et que se passera-t-il si l'on essaie d'écrire dans ce tableau ?"

Réponse : Oui, il est possible de déclarer comme Array<out Array<Int>>, mais ce tableau devient seulement en lecture (read-only) :

val arr: Array<out Array<Int>> = Array(1) { Array(1) { 0 } } arr[0] = arrayOf(1, 2, 3) // Erreur de compilation !

Essayer d'écrire une valeur entraînera une erreur — un tableau générique avec un paramètre out ne permet pas d'écrire des éléments, car cela violerait la sécurité des types.

Exemples d'erreurs réelles dues à l'ignorance des subtilités du sujet


Histoire

L'équipe a essayé de créer un tableau d'objets génériques avec un type de paramètre out, puis de mettre des valeurs via set(index, value). Le code compilait, mais a entraîné une erreur à l'exécution, et plusieurs fonctions se sont révélées non fonctionnelles.


Histoire

Une fois, lors de la migration d'une bibliothèque de Java à Kotlin, des types génériques ont été laissés avec des wildcards (? extends ...), et en Kotlin, les types ont simplement été copiés sans modification en out/in. Résultat — la compilation a échoué, et lors de la "parcours", l'erreur est survenue à l'exécution, compliquant le processus de débogage.


Histoire

Utilisation de la variance in/out avec une classe personnalisée, mais les modificateurs ont été inversés, déclarant l'interface Stack<in T> au lieu de Stack<out T>. Cela a conduit à l'incapacité de retourner des éléments de la pile : la signature de la méthode violait le contrat système out/in.