Menu
Coddy logo textTech

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
false

Objekte, 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
true

Eine 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>]
challenge icon

Aufgabe

Einfach

Zwei 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()}")
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Kotlin-Playground