Companion-Objekte
Teil des Abschnitts Objektorientierte Programmierung der Kotlin-Journey von Coddy. Lektion 13 von 57.
Einige Mitglieder gehören zur Klasse als Ganzes und nicht zu einer einzelnen Instanz. Kotlin legt sie in einem companion object innerhalb der Klasse ab, und du rufst sie über den Klassennamen auf:
class Temperature(val celsius: Double) {
companion object {
fun fromFahrenheit(f: Double) = Temperature((f - 32) * 5 / 9)
}
}Innerhalb von main:
val boiling = Temperature.fromFahrenheit(212.0)
println(boiling.celsius)Ausgabe:
100.0fromFahrenheit ist eine Fabrikfunktion: eine Funktion, die Instanzen erstellt. Im Gegensatz zu einem Konstruktor hat sie einen beschreibenden Namen und kann statt eines Objekts null zurückgeben. Mit einem private constructor ist die Fabrik der einzige Zugang, sodass sie die Eingabe zuerst prüfen und bereinigen kann:
class Username private constructor(val text: String) {
companion object {
fun of(raw: String): Username? {
val clean = raw.trim().lowercase()
return if (clean.length in 3..12) Username(clean) else null
}
}
}Innerhalb von main:
println(Username.of(" Ada_L ")?.text)
println(Username.of("x")?.text)
// Username("x") // Fehler: der Konstruktor ist privatAusgabe:
ada_l
nullDas Companion-Objekt gehört zur Klasse, kann also den privaten Konstruktor aufrufen, und der eigene Code der Klasse verwendet die Member des Companion-Objekts ohne Präfix. Hier erhält jedes Ticket die nächste Nummer von einem Zähler, den alle Tickets gemeinsam nutzen:
class Ticket(val holder: String) {
val number = nextNumber++
companion object {
private var nextNumber = 1
val issued: Int
get() = nextNumber - 1
}
}Innerhalb von main:
val a = Ticket("Ada")
val b = Ticket("Bo")
println("${a.holder} #${a.number}, ${b.holder} #${b.number}")
println("issued: ${Ticket.issued}")Ausgabe:
Ada #1, Bo #2
issued: 2Kotlin hat kein static-Schlüsselwort. Die Member des Companion übernehmen diese Rolle: eine Kopie für die gesamte Klasse, erreichbar als ClassName.member. Wie jedes Objekt wird der Companion einmal erstellt, wenn die Klasse zum ersten Mal verwendet wird. Füge dort Dinge ein, die nicht von einer einzelnen Instanz abhängen, etwa Factory-Methoden und gemeinsam genutzte Zähler; alles, was die Daten einer Instanz liest, bleibt ein normales Member.
class User private constructor(val name: String) {
fun greet() = "hi $name" // benötigt eine Instanz: user.greet()
companion object {
fun guest() = User("guest") // gehört zur Klasse: User.guest()
}
}Aufgabe
EinfachVervollständige die Klasse Coupon. Ihr Konstruktor ist privat, daher werden Coupons von der Factory Coupon.create(code, percent) im Companion-Objekt erstellt. Sie gibt null zurück, sofern der Code nicht aus 4 bis 10 Großbuchstaben und Ziffern besteht oder der Prozentsatz nicht zwischen 1 und 90 liegt. Das Companion-Objekt zählt außerdem die von ihm erstellten Coupons in created, das Code außerhalb des Objekts lesen, aber nicht ändern kann. apply(price) gibt den um den Rabatt verminderten Preis abgerundet zurück (price * (100 - percent) / 100).
Der bereitgestellte Code liest Zeilen SAVE10 10 200 (Code, Prozentsatz, Preis). Er gibt SAVE10: 180 oder invalid coupon SAVE10 aus und schließlich created: mit der Anzahl.
Dein Code gehört in Coupon.kt. Main.kt enthält den bereitgestellten Eingabe-/Ausgabecode und kann nicht bearbeitet werden.
Probier es selbst
fun main() {
// Bereitgestellter Ein-/Ausgabe-Code: belasse ihn unverändert
val input = generateSequence(::readLine).toList()
for (line in input) {
val (code, percent, price) = line.split(" ")
val coupon = Coupon.create(code, percent.toInt())
if (coupon == null) println("invalid coupon $code") else println("${coupon.code}: ${coupon.apply(price.toInt())}")
}
println("created: ${Coupon.created}")
}
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 ausleihen3Objekte und Companion-Objekte
ObjektdeklarationenCompanion-ObjekteKonstanten mit constObjektausdrückeRückblick – FahrkartenschalterÜbe selbstständig: Kotlin-Playground