Délégation avec by
Fait partie de la section Programmation orientée objet du Journey Kotlin de Coddy. Leçon 46 sur 57.
Délégation signifie qu’un objet confie une tâche à un autre objet au lieu d’en hériter. Kotlin la prend directement en charge : class Polite(private val inner: Greeter) : Greeter by inner implémente Greeter en transmettant chaque appel à inner, et la classe ne redéfinit que ce qu’elle modifie :
interface Greeter {
fun hello(name: String): String
fun bye(name: String): String
}
class English : Greeter {
override fun hello(name: String) = "Hello, $name"
override fun bye(name: String) = "Bye, $name"
}
class Polite(private val inner: Greeter) : Greeter by inner {
override fun hello(name: String) = inner.hello(name) + ", pleased to meet you"
}Dans main :
val g = Polite(English())
println(g.hello("Ada"))
println(g.bye("Ada"))Sortie :
Hello, Ada, pleased to meet you
Bye, AdaLa délégation est une alternative à l’héritage. Elle fonctionne avec des interfaces dont les implémentations sont finales, et le délégué peut être n’importe quel objet choisi pendant l’exécution du programme. Cet ensemble compte chaque add et transmet tout le reste à un MutableSet normal :
class CountingSet<T>(private val inner: MutableSet<T> = mutableSetOf()) : MutableSet<T> by inner {
var attempts = 0
private set
override fun add(element: T): Boolean {
attempts++
return inner.add(element)
}
}Dans main :
val tags = CountingSet<String>()
tags.add("kotlin")
tags.add("jvm")
tags.add("kotlin")
println("${tags.size} tags after ${tags.attempts} adds, contains jvm: ${"jvm" in tags}")Sortie :
2 tags after 3 adds, contains jvm: trueLes propriétés déléguées confient l’accesseur et le mutateur d’une propriété à un autre objet, toujours avec by. lazy du chapitre 2 est un tel délégué, et une map peut en être un autre : chaque propriété lit l’entrée portant son propre nom :
class User(values: Map<String, Any?>) {
val name: String by values
val age: Int by values
}Dans main :
val u = User(mapOf("name" to "Ada", "age" to 36))
println("${u.name} is ${u.age}")Sortie :
Ada is 36Vous pouvez écrire votre propre délégué de propriété : une classe avec un operator fun getValue, ainsi que setValue pour une var. Kotlin les appelle à chaque lecture et écriture de la propriété. Celui-ci supprime les espaces superflus de chaque texte assigné et compte les modifications :
import kotlin.reflect.KProperty
class Trimmed {
private var value = ""
var changes = 0
operator fun getValue(thisRef: Any?, property: KProperty<*>) = value
operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: String) {
value = newValue.trim()
changes++
}
}
class Form {
val nameField = Trimmed()
var name: String by nameField
}Dans main :
val form = Form()
form.name = " Ada "
form.name = "Ada L. "
println("[${form.name}] after ${form.nameField.changes} changes")Sortie :
[Ada L.] after 2 changesDéfi
FacileL’interface Storage (avec put, get et keys) et son implémentation MapStorage sont fournies. Écrivez deux décorateurs avec délégation de classe, chacun ne redéfinissant que put : LoggingStorage(inner) enregistre put a=1 dans sa liste log en lecture seule et transmet l’appel, tandis que LimitedStorage(inner, max) refuse (renvoie false) d’ajouter une nouvelle clé dès que max clés existent, alors que la mise à jour des clés existantes reste autorisée. Tout le reste est transmis avec by.
Le code fourni enveloppe un MapStorage dans un LoggingStorage et, lorsque la première ligne contient une limite positive, dans un LimitedStorage. Il exécute les commandes put a 1, get a et keys, en affichant ok, refused, la valeur ou missing, ainsi que la liste des clés, puis finalement log: put a=1, ....
Votre code doit se trouver dans LoggingStorage.kt, LimitedStorage.kt, Storage.kt et MapStorage.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 base = MapStorage()
val logging = LoggingStorage(base)
val limit = input[0].toInt()
val storage: Storage = if (limit > 0) LimitedStorage(logging, limit) else logging
for (cmd in input.drop(1)) {
val p = cmd.split(" ")
when (p[0]) {
"put" -> println(if (storage.put(p[1], p[2])) "ok" else "refused")
"get" -> println(storage.get(p[1]) ?: "missing")
else -> println(storage.keys())
}
}
println("log: " + logging.log.joinToString(", ").ifEmpty { "empty" })
}
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