Generics en Kotlin : pourquoi et comment les utiliser : pour débutants

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 et comment les utiliser



Lors du développement en Kotlin, vous rencontrez souvent le besoin d'écrire du code qui peut fonctionner avec différents types de données. Au lieu de créer des fonctions ou des classes séparées pour Int, String, Double, etc., vous pouvez utiliser le mécanisme des Generics (génériques). Cet article explique ce que sont les Generics en Kotlin, pourquoi ils sont nécessaires et comment les appliquer en pratique.



Que sont les Generics et pourquoi sont-ils nécessaires ?



Les Generics (génériques) sont un outil qui permet d'écrire des classes, des interfaces et des fonctions fonctionnant avec des types de données arbitraires. Imaginez que vous créez une boîte dans laquelle vous pouvez mettre n'importe quoi : un livre, un téléphone ou de la nourriture. Au lieu de faire une boîte séparée pour chaque objet, vous en faites une universelle.



Les principaux avantages de l'utilisation des Generics :

  • Sécurité des types (Type Safety) — le compilateur vérifie les types au moment de la compilation, ce qui évite les erreurs d'exécution.
  • Réutilisation du code — un code écrit une seule fois fonctionne avec n'importe quel type.
  • Lisibilité — le code devient plus compréhensible car il indique explicitement avec quels types il fonctionne.


Sans Generics, vous devriez utiliser Any et effectuer constamment des conversions de types, ce qui est source d'erreurs. Avec les Generics, le compilateur surveille lui-même la validité des types.



Bases des Generics en Kotlin : paramètres de type



En Kotlin, les Generics sont implémentés via des paramètres de type. Ils sont spécifiés entre chevrons <>. Les lettres les plus couramment utilisées sont T (Type), E (Element), K (Key), V (Value), mais vous pouvez utiliser n'importe quel nom.



Fonctions génériques



Commençons par un exemple simple — une fonction qui retourne l'argument qui lui est passé :



fun <T> identity(value: T): T {    return value}

fun main() { val number: Int = identity(42) val text: String = identity("Bonjour") println(number) // 42 println(text) // Bonjour}


Ici, <T> avant le nom de la fonction déclare le paramètre de type. Le type T est déduit automatiquement de l'argument, vous n'avez donc pas besoin de le spécifier explicitement.



Classes génériques



Les classes peuvent également être génériques. Créons un conteneur simple :



class Box<T>(private val item: T) {    fun getItem(): T {        return item    }}

fun main() { val stringBox = Box("Livre") val intBox = Box(123)

println(stringBox.getItem()) // Livre println(intBox.getItem()) // 123}


La classe Box<T> peut stocker un objet de n'importe quel type. Lors de la création d'une instance, nous spécifions le type concret, et le compilateur garantit que getItem() retournera exactement ce type.



Limitations de types (Type Bounds)



Parfois, il est nécessaire de limiter les types pouvant être utilisés dans un Generic. Par exemple, pour qu'une fonction fonctionne uniquement avec des nombres. Pour cela, on utilise des limitations (bounds) avec le mot-clé :.



Limite supérieure (Upper Bound)



fun <T : Number> sum(a: T, b: T): Double {    return a.toDouble() + b.toDouble()}

fun main() { println(sum(10, 20)) // 30.0 println(sum(3.5, 2.5)) // 6.0 // println(sum("A", "B")) // Erreur de compilation}


Ici, T : Number signifie que le type T doit être un héritier de la classe Number. Int, Double, Float conviennent, mais String non.



Limitations multiples



Si vous avez besoin de plusieurs limitations, utilisez where :



interface Printable {    fun print()}

fun <T>

Blogs

Book Recommendations