Menu
Coddy logo textTech

Classes génériques

Fait partie de la section Programmation orientée objet du Journey Kotlin de Coddy. Leçon 38 sur 57.

Une classe peut également avoir des paramètres de type. Box<T> contient une valeur de n’importe quel type, et chaque instance fixe le type : une Box<Int> n’accepte que des valeurs de type Int :

class Box<T>(var value: T) {
    fun describe() = "box with $value"
}

À l’intérieur de main :

val numberBox = Box(42)
val textBox = Box("hello")
numberBox.value += 1
println(numberBox.describe())
println(textBox.describe())
// numberBox.value = "x"   // erreur : un String n'est pas un Int

Sortie :

box with 43
box with hello

Les conteneurs sont les classes génériques typiques. Une implémentation de Stack<T> fonctionne pour chaque type d’élément, et le compilateur vérifie ce qui y entre et ce qui en sort :

class Stack<T> {
    private val items = mutableListOf<T>()
    val size: Int
        get() = items.size

    fun push(item: T) { items.add(item) }
    fun pop(): T? = items.removeLastOrNull()
    fun peek(): T? = items.lastOrNull()
}

Dans main :

val stack = Stack<String>()
stack.push("a")
stack.push("b")
println(stack.pop())
println(stack.peek())
println(stack.size)

Sortie :

b
a
1

Le paramètre de type est déduit des arguments du constructeur lorsqu’il y en a, comme dans Box(42). Sinon, écrivez-le : Stack<String>(). Les instances ayant des paramètres de type différents sont des types différents, ainsi une Stack<String> n’est jamais confondue avec une Stack<Int>. Les fonctions d’une classe générique peuvent également ajouter leurs propres paramètres de type :

class Box<T>(val value: T) {
    fun <R> map(f: (T) -> R): Box<R> = Box(f(value))
}

Dans main :

val length = Box("kotlin").map { it.length }
println(length.value + 1)

Sortie :

7

Les interfaces génériques suivent les mêmes règles. Une classe peut en implémenter une de manière générique, ou pour un type d’argument particulier :

interface Repository<T> {
    fun add(item: T)
    fun all(): List<T>
}
class MemoryRepository<T> : Repository<T> {
    private val items = mutableListOf<T>()
    override fun add(item: T) { items.add(item) }
    override fun all() = items.toList()
}
class TagRepository : Repository<String> {
    private val tags = sortedSetOf<String>()
    override fun add(item: String) { tags.add(item.lowercase()) }
    override fun all() = tags.toList()
}

À l’intérieur de main :

val ids = MemoryRepository<Int>()
ids.add(3); ids.add(1)
val tags = TagRepository()
tags.add("Kotlin"); tags.add("JVM")
println("${ids.all()} ${tags.all()}")

Sortie :

[3, 1] [jvm, kotlin]
challenge icon

Défi

Facile

Écrivez la classe générique History<T>(limit) qui mémorise les derniers éléments limit. add(item) ajoute un élément et supprime le plus ancien lorsqu’il y en a plus de limit. latest est l’élément le plus récent ou null, et all() renvoie les éléments du plus ancien au plus récent sous la forme d’une liste en lecture seule. Écrivez ensuite l’extension History<Int>.average(), qui existe uniquement pour les historiques de nombres et renvoie la moyenne entière des nombres conservés (0 lorsque l’historique est vide).

Le code fourni lit la limite, une ligne de mots et une ligne de nombres. Il remplit un History<String> et un History<Int>, puis affiche les mots conservés, le dernier mot, les nombres conservés, leur moyenne et empty latest: null.

Votre code doit être placé dans History.kt. Main.kt contient le code d’entrée/sortie fourni et ne peut pas être modifié.

Essayez vous-même

fun main() {
    // Code d'entrée/sortie fourni : le garder tel quel
    val input = generateSequence(::readLine).toList()
    val limit = input[0].toInt()
    val words = History<String>(limit)
    for (w in input[1].split(" ")) words.add(w)
    val numbers = History<Int>(limit)
    for (n in input[2].split(" ")) numbers.add(n.toInt())
    println("words: ${words.all()}")
    println("latest word: ${words.latest}")
    println("numbers: ${numbers.all()}")
    println("average: ${numbers.average()}")
    println("empty latest: ${History<Char>(3).latest}")
}
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation orientée objet

Entraînez-vous par vous-même : Playground Kotlin