Delegation mit by
Teil des Abschnitts Objektorientierte Programmierung der Kotlin-Journey von Coddy. Lektion 46 von 57.
Delegation bedeutet, dass ein Objekt Arbeit an ein anderes Objekt übergibt, anstatt sie zu erben. Kotlin unterstützt dies direkt: class Polite(private val inner: Greeter) : Greeter by inner implementiert Greeter, indem es jeden Aufruf an inner weiterleitet, und die Klasse überschreibt nur das, was sie ändert:
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"
}Innerhalb von main:
val g = Polite(English())
println(g.hello("Ada"))
println(g.bye("Ada"))Ausgabe:
Hello, Ada, pleased to meet you
Bye, AdaDelegation ist eine Alternative zur Vererbung. Sie funktioniert mit Schnittstellen, deren Implementierungen final sind, und der Delegat kann jedes Objekt sein, das während der Programmausführung ausgewählt wird. Dieses Set zählt jedes add und leitet alles andere an ein normales MutableSet weiter:
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)
}
}Innerhalb von 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}")Ausgabe:
2 tags after 3 adds, contains jvm: trueDelegierte Eigenschaften übergeben den Getter und Setter einer Eigenschaft an ein anderes Objekt, wiederum mit by. lazy aus Kapitel 2 ist ein solcher Delegat, und eine Map kann ein weiterer sein: Jede Eigenschaft liest den Eintrag mit ihrem eigenen Namen:
class User(values: Map<String, Any?>) {
val name: String by values
val age: Int by values
}Innerhalb von main:
val u = User(mapOf("name" to "Ada", "age" to 36))
println("${u.name} is ${u.age}")Ausgabe:
Ada is 36Du kannst deinen eigenen Property-Delegaten schreiben: eine Klasse mit einem operator fun getValue sowie setValue für ein var. Kotlin ruft sie bei jedem Lesen und Schreiben der Property auf. Dieser entfernt bei jedem zugewiesenen Text die Leerzeichen und zählt die Änderungen:
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
}Innerhalb von main:
val form = Form()
form.name = " Ada "
form.name = "Ada L. "
println("[${form.name}] after ${form.nameField.changes} changes")Ausgabe:
[Ada L.] after 2 changesAufgabe
EinfachDie Schnittstelle Storage (mit put, get und keys) und ihre Implementierung MapStorage werden bereitgestellt. Schreibe zwei Decorators mit Klassendelegation, die jeweils nur put überschreiben: LoggingStorage(inner) protokolliert put a=1 in seiner schreibgeschützten log-Liste und leitet den Aufruf weiter, und LimitedStorage(inner, max) verweigert (gibt false zurück) das Hinzufügen eines neuen Schlüssels, sobald max Schlüssel vorhanden sind, während Aktualisierungen bestehender Schlüssel weiterhin erlaubt sind. Alles andere wird mit by weitergeleitet.
Der bereitgestellte Code verpackt einen MapStorage in einen LoggingStorage und bei einem positiven Limit in der ersten Zeile zusätzlich in einen LimitedStorage. Er führt die Befehle put a 1, get a und keys aus und gibt ok, refused, den Wert oder missing sowie die Schlüsselliste aus und schließlich log: put a=1, ....
Dein Code gehört in LoggingStorage.kt, LimitedStorage.kt, Storage.kt und MapStorage.kt. Main.kt enthält den bereitgestellten Eingabe-/Ausgabecode und darf nicht bearbeitet werden.
Probier es selbst
fun main() {
// Bereitgestellter Ein-/Ausgabecode: belasse ihn wie er ist
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" })
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Klassen und Objekte
Arbeiten mit DateienKlassen definierenPrimärkonstruktoreninit-Blöcke und ValidierungSekundärkonstruktorenRückblick – Bibliotheksausweis4Vererbung
Offene Klassen und UnterklassenÜberschreiben von Membernsuper und KonstruktorenAbstrakte KlassenRückblick – Mitarbeiterverzeichnis2Eigenschaften und Kapselung
Benutzerdefinierte ZugriffsmethodenSichtbarkeitsmodifikatorenPrivate Setterlazy und lateinitWiederholung – Bankkonto5Schnittstellen
Schnittstellen deklarierenStandardmethodenMehrere SchnittstellenSchnittstellen als TypenRückblick – Zahlungsmethoden8Generics
Generische FunktionenGenerische KlassenTypbeschränkungenVarianz mit in und outRückblick – Prioritätswarteschlange11Projekt: Bibliotheksverwaltung
Bücher und MitgliederBücher ausleihenÜbe selbstständig: Kotlin-Playground