equals, hashCode, toString
Teil des Abschnitts Objektorientierte Programmierung der Kotlin-Journey von Coddy. Lektion 35 von 57.
Jede Klasse erbt toString(), equals() und hashCode() von Any. Die geerbten Versionen arbeiten mit Identität: toString() liefert den Klassennamen und einen Hashcode, und equals() ist nur für dasselbe Objekt true. Das Überschreiben von toString() liefert Objekten einen lesbaren Text, der von println und String-Templates verwendet wird:
class Money(val cents: Int) {
override fun toString() = "$%d.%02d".format(cents / 100, cents % 100)
}Innerhalb von main:
val price = Money(1250)
println(price)
println("total: ${Money(99)}")Ausgabe:
$12.50
total: $0.99== ruft equals() auf, während === prüft, ob zwei Referenzen auf dasselbe Objekt verweisen. Um nach Wert zu vergleichen, überschreibst du equals. Sein Parameter ist Any?, daher wird zuerst der Typ geprüft und anschließend werden die relevanten Eigenschaften verglichen:
class Isbn(code: String) {
val digits = code.filter { it.isDigit() }
override fun equals(other: Any?) = other is Isbn && other.digits == digits
override fun hashCode() = digits.hashCode()
}In main:
val a = Isbn("978-0-441-17271-9")
val b = Isbn("9780441172719")
println(a == b)
println(a === b)Ausgabe:
true
falseObjekte, die gleich sind, müssen gleiche Hashcodes haben. HashSet und HashMap finden Einträge zuerst über hashCode() und rufen erst danach equals() auf. Wenn du also equals ohne hashCode überschreibst, wirken gleiche Objekte für sie unterschiedlich. Überschreibe immer beide, berechnet aus denselben Eigenschaften:
class Isbn(code: String) {
val digits = code.filter { it.isDigit() }
override fun equals(other: Any?) = other is Isbn && other.digits == digits
override fun hashCode() = digits.hashCode()
override fun toString() = "ISBN $digits"
}Innerhalb von main:
val shelf = hashSetOf(Isbn("978-0-441-17271-9"), Isbn("9780441172719"), Isbn("0-14-143951-3"))
println(shelf.size)
println(Isbn("9780441172719") in shelf)Ausgabe:
2
trueEine Datenklasse erzeugt alle drei aus ihren Konstruktor-Eigenschaften. Schreibe sie selbst, wenn die Gleichheit weniger Eigenschaften oder eine normalisierte Form verwenden soll, etwa eine E-Mail-Adresse, bei der die Groß- und Kleinschreibung ignoriert wird:
class User(val email: String, val name: String) {
private val key = email.trim().lowercase()
override fun equals(other: Any?) = other is User && other.key == key
override fun hashCode() = key.hashCode()
override fun toString() = "$name <$key>"
}Innerhalb von main:
val users = listOf(User("Ada@Example.com", "Ada"), User("ada@example.com ", "A. Lovelace"))
println(users.distinct())Ausgabe:
[Ada <ada@example.com>]Aufgabe
EinfachZwei Produkte sind identisch, wenn ihr sku übereinstimmt, unabhängig davon, was die anderen Felder enthalten. Überschreibe equals() und hashCode() in Product ausschließlich anhand von sku und toString(), sodass ein Produkt als A1 tea 450 ausgegeben wird. Vervollständige anschließend mergeCatalog(products), wobei das erste Vorkommen jedes Produkts in der ursprünglichen Reihenfolge beibehalten wird.
Der bereitgestellte Code liest Produkte als Zeilen im Format sku,name,price sowie eine letzte Zeile mit einer nachzuschlagenden sku ein. Er gibt jedes beibehaltene Produkt, removed: 2 und A1 in catalog: true aus, wobei die sku in einem HashSet nachgeschlagen wird.
Dein Code gehört in Product.kt und Functions.kt. Main.kt enthält den bereitgestellten Eingabe-/Ausgabecode und darf nicht bearbeitet werden.
Probier es selbst
fun main() {
// Gelieferter Ein-/Ausgabecode: belasse ihn wie er ist
val input = generateSequence(::readLine).toList()
val products = input.dropLast(1).map { line ->
val (sku, name, price) = line.split(",")
Product(sku, name, price.toInt())
}
val kept = mergeCatalog(products)
for (p in kept) println(p)
println("removed: ${products.size - kept.size}")
val probe = Product(input.last(), "?", 0)
println("${input.last()} in catalog: ${probe in kept.toHashSet()}")
}
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 – Mitarbeiterverzeichnis7Spezielle Klassen
DatenklassenEnum-KlassenVersiegelte Hierarchienequals, hashCode, toStringRückblick – Bestellverfolgung2Eigenschaften 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