Menu
Coddy logo textTech

equals, hashCode, toString

Parte de la sección Programación orientada a objetos del Journey de Kotlin de Coddy. Lección 35 de 57.

Toda clase hereda toString(), equals() y hashCode() de Any. Las versiones heredadas funcionan según la identidad: toString() proporciona el nombre de la clase y un código hash, y equals() es true únicamente para el mismo objeto. Sobrescribir toString() proporciona a los objetos un texto legible, utilizado por println y las plantillas de cadenas:

class Money(val cents: Int) {
    override fun toString() = "$%d.%02d".format(cents / 100, cents % 100)
}

Dentro de main:

val price = Money(1250)
println(price)
println("total: ${Money(99)}")

Salida:

$12.50
total: $0.99

== llama a equals(), mientras que === comprueba si dos referencias apuntan al mismo objeto. Para comparar por valor, sobrescribe equals. Su parámetro es Any?, por lo que primero comprueba el tipo y después compara las propiedades relevantes:

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()
}

Dentro de main:

val a = Isbn("978-0-441-17271-9")
val b = Isbn("9780441172719")
println(a == b)
println(a === b)

Salida:

true
false

Los objetos que son iguales deben tener códigos hash iguales. HashSet y HashMap buscan las entradas primero mediante hashCode() y solo después llaman a equals(), por lo que sobrescribir equals sin hashCode hace que los objetos iguales parezcan diferentes en ellos. Sobrescribe siempre ambos, calculados a partir de las mismas propiedades:

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"
}

Dentro de 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)

Salida:

2
true

Una clase de datos genera las tres a partir de las propiedades de su constructor. Escríbelos tú mismo cuando la igualdad deba usar menos propiedades o una forma normalizada, como una dirección de correo electrónico que ignore las mayúsculas y minúsculas:

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>"
}

Dentro de main:

val users = listOf(User("Ada@Example.com", "Ada"), User("ada@example.com ", "A. Lovelace"))
println(users.distinct())

Salida:

[Ada <ada@example.com>]
challenge icon

Desafío

Fácil

Dos productos son iguales cuando sus sku coinciden, sin importar lo que indiquen los demás campos. Sobrescribe equals() y hashCode() en Product usando únicamente sku, y toString() para que un producto se muestre como A1 tea 450. Después completa mergeCatalog(products), que conserva la primera aparición de cada producto en el orden original.

El código proporcionado lee los productos como líneas con el formato sku,name,price y una última línea con un sku que se debe buscar. Imprime cada producto conservado, removed: 2 y A1 in catalog: true, buscando el sku en un HashSet.

Tu código va en Product.kt y Functions.kt. Main.kt contiene el código proporcionado de entrada/salida y no se puede editar.

Pruébalo tú mismo

fun main() {
    // Código de entrada/salida suministrado: déjalo tal como está
    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 iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación orientada a objetos

Practica por tu cuenta: Playground de Kotlin