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 IntSortie :
box with 43
box with helloLes 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
1Le 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 :
7Les 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]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}")
}
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
1Classes et objets
Travailler avec des fichiersDéfinir des classesConstructeurs primairesBlocs init et validationConstructeurs secondairesRécapitulatif - Carte de bibliothèque4Héritage
Classes ouvertes et sous-classesRedéfinition des membressuper et constructeursClasses abstraitesRécapitulatif – Annuaire du personnel2Propriétés et encapsulation
Accesseurs personnalisésModificateurs de visibilitéSetters privéslazy et lateinitRécapitulatif - Compte bancaire5Interfaces
Déclarer des interfacesMéthodes par défautInterfaces multiplesInterfaces en tant que typesRécapitulatif - Méthodes de paiement8Génériques
Fonctions génériquesClasses génériquesContraintes de typeVariance avec in et outRécapitulatif - File de priorité11Projet : Gestion de bibliothèque
Livres et membresEmprunter des livresEntraînez-vous par vous-même : Playground Kotlin