Generics en Kotlin : pourquoi sont-ils nécessaires et comment les utiliser en pratique

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Generics en Kotlin : pourquoi sont-ils nécessaires et comment les utiliser en pratique



Les Generics (génériques) sont l'une des fonctionnalités clés du langage Kotlin, qui permet d'écrire un code plus flexible, type-safe et réutilisable. Si vous avez déjà écrit List<String> ou Map<Int, User>, vous avez déjà rencontré les génériques. Mais pourquoi sont-ils réellement nécessaires et comment en tirer le maximum ? Dans cet article, nous aborderons la théorie et passerons à la pratique avec des exemples concrets.



Le problème résolu par les Generics



Imaginez que vous deviez écrire une fonction qui retourne le premier élément d'une liste. Sans génériques, vous devriez écrire une fonction séparée pour chaque type de données :



fun getFirstString(list: List<String>): String? = list.firstOrNull()fun getFirstInt(list: List<Int>): Int? = list.firstOrNull()fun getFirstUser(list: List<User>): User? = list.firstOrNull()


Cela entraîne une duplication de code. Utiliser Any résoudrait le problème, mais vous perdriez l'information sur le type au moment de la compilation :



fun getFirst(list: List<Any>): Any? = list.firstOrNull()

// Il faudrait faire un castval name: String? = getFirst(strings) as? String


Les Generics résolvent ce problème élégamment :



fun <T> getFirst(list: List<T>): T? = list.firstOrNull()

// Le compilateur connaît le type !val name: String? = getFirst(listOf("Alice", "Bob"))val age: Int? = getFirst(listOf(25, 30))


Maintenant, une seule fonction fonctionne avec n'importe quel type, tout en conservant une sécurité de type complète.



Les bases des Generics en Kotlin : syntaxe et déclaration



Les génériques sont déclarés avec des chevrons <T>. La lettre T est simplement une convention ; vous pouvez utiliser n'importe quel nom : E (élément), K (clé), V (valeur).



Classes génériques



class Box<T>(val value: T) {    fun getContent(): T = value}

fun main() { val stringBox = Box("Hello") val intBox = Box(42) println(stringBox.getContent()) // Hello println(intBox.getContent()) // 42}


Fonctions génériques



fun <T> printItem(item: T) {    println("Item: $item")}

// On peut spécifier le type explicitementprintItem<String>("Kotlin")// Ou se fier à l'inférence de typeprintItem(100)


Interfaces génériques



interface Repository<T> {    fun getById(id: Int): T    fun save(item: T): Boolean}

class UserRepository : Repository<User> { override fun getById(id: Int): User { /* ... */ } override fun save(item: User): Boolean { /* ... */ }}


Variance : in, out et l'astérisque *



L'une des fonctionnalités les plus puissantes de Kotlin est la variance. Elle répond à la question : List<String> peut-il être un sous-type de List<Any> ?



Covariance (out)



Utilisez out lorsque votre type générique est uniquement retourné (producteur). Par exemple, List<T> est covariant :



// Vous pouvez assigner List<String> à une variable List<Any>val strings: List<String> = listOf("a", "b")val anyList: List<Any> = strings // OK !

// Mais vous ne pouvez pas ajouter d'éléments// anyList.add(1) // Erreur de compilation !


Exemple personnalisé :



class Producer<out T>(private val value: T) {    fun produce(): T = value}

fun main() { val stringProducer: Producer<String> = Producer("Hello") val anyProducer: Producer<Any> = stringProducer // Fonctionne ! println(anyProducer.produce())}


Contravariance (in)



Utilisez in lorsque le type est uniquement accepté (consommateur). Par exemple, Comparable<T> :





                
                

Blogs

Book Recommendations