Menu
Coddy logo textTech

equals, hashCode, toString

Fait partie de la section Programmation orientée objet du Journey Kotlin de Coddy. Leçon 35 sur 57.

Chaque classe hérite de toString(), equals() et hashCode() de Any. Les versions héritées fonctionnent sur l’identité : toString() donne le nom de la classe et un code de hachage, et equals() vaut true uniquement pour le même objet. Redéfinir toString() fournit aux objets un texte lisible, utilisé par println et les modèles de chaînes :

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

À l’intérieur de main :

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

Résultat :

$12.50
total: $0.99

== appelle equals(), tandis que === vérifie si deux références pointent vers le même objet. Pour comparer par valeur, redéfinissez equals. Son paramètre est Any? : la méthode vérifie donc d’abord le type, puis compare les propriétés pertinentes :

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

Dans main :

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

Sortie :

true
false

Les objets qui sont égaux doivent avoir des codes de hachage égaux. HashSet et HashMap recherchent d’abord les entrées avec hashCode(), puis appellent equals(). Ainsi, redéfinir equals sans hashCode fait paraître les objets égaux différents pour ces collections. Redéfinissez toujours les deux, en les calculant à partir des mêmes propriétés :

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

Dans 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)

Sortie :

2
true

Une classe de données génère les trois à partir de ses propriétés de constructeur. Écrivez-les vous-même lorsque l’égalité doit utiliser moins de propriétés ou une forme normalisée, comme une adresse e-mail qui ignore la casse :

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

À l’intérieur de main :

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

Résultat :

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

Défi

Facile

Deux produits sont identiques lorsque leur sku correspond, quels que soient les autres champs. Redéfinissez equals() et hashCode() dans Product en vous basant uniquement sur sku, ainsi que toString() afin qu’un produit s’affiche comme A1 tea 450. Complétez ensuite mergeCatalog(products), qui conserve la première occurrence de chaque produit dans l’ordre d’origine.

Le code fourni lit les produits sous forme de lignes sku,name,price, ainsi qu’une dernière ligne contenant un sku à rechercher. Il affiche chaque produit conservé, removed: 2 et A1 in catalog: true, en recherchant le sku dans un HashSet.

Votre code doit être placé dans Product.kt et Functions.kt. Main.kt contient le code fourni pour les entrées et sorties et ne peut pas être modifié.

Essayez vous-même

fun main() {
    // Code d'entrée/sortie fourni : le laisser tel quel
    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 iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation orientée objet

Entraînez-vous par vous-même : Playground Kotlin