← Назад к вопросам

Как переопределять метод equals()

2.0 Middle🔥 81 комментариев
#JVM и память#Kotlin основы

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как переопределять метод equals() в Kotlin

equals() — один из самых важных методов, определяющий сравнение объектов. Неправильная реализация приводит к затруднительным багам.

Контракт equals()

  • Рефлексивность: x.equals(x) = true
  • Симметричность: если x.equals(y), то y.equals(x)
  • Транзитивность: если x.equals(y) и y.equals(z), то x.equals(z)
  • Консистентность: повторные вызовы возвращают одинаковый результат
  • Сравнение с null: x.equals(null) = false

Правильная реализация

class Person(val name: String, val age: Int) {
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other !is Person) return false
        return name == other.name && age == other.age
    }
    
    override fun hashCode(): Int {
        return name.hashCode() + age.hashCode()
    }
}

Три критических шага

Шаг 1: Проверка идентичности (оптимизация)

if (this === other) return true

Шаг 2: Проверка типа

if (other !is Person) return false

Шаг 3: Сравнение полей

return name == other.name && age == other.age

Критическая ошибка: забыть hashCode()

Это ОБЯЗАТЕЛЬНО! Если переопределить equals(), нужно переопределить hashCode():

// Плохо - нарушает контракт
class Point(val x: Int, val y: Int) {
    override fun equals(other: Any?): Boolean {
        return other is Point && x == other.x && y == other.y
    }
    // hashCode() не переопределён!
}

// Использование в Set
val set = hashSetOf(Point(1, 2))
println(Point(1, 2) in set) // false! Хотя объекты равны

Почему? Set и Map используют hashCode() для поиска элементов. Если hashCode() не согласован с equals(), объекты теряются в коллекциях.

Типичные ошибки

Ошибка 1: Сравнение с null без проверки

// Плохо
if (other.name == name) return true // NPE если other = null

// Правильно
if (other !is Person) return false
return other.name == name

Ошибка 2: Использовать == для object references неправильно

val p1 = Person("John", 30)
val p2 = Person("John", 30)
println(p1 == p2) // true (вызывает equals())
println(p1 === p2) // false (разные ссылки)

Data класс

В Kotlin есть простой способ — data класс автоматически создаёт equals() и hashCode():

data class Person(val name: String, val age: Int)

val p1 = Person("John", 30)
val p2 = Person("John", 30)
println(p1 == p2) // true
println(p1.hashCode() == p2.hashCode()) // true

Наследование

Будьте осторожны при наследовании — новые поля должны учитываться:

open class Animal(val name: String) {
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other !is Animal) return false
        return name == other.name
    }
    override fun hashCode() = name.hashCode()
}

class Dog(name: String, val breed: String) : Animal(name) {
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other !is Dog) return false
        return super.equals(other) && breed == other.breed
    }
    override fun hashCode() = super.hashCode() + breed.hashCode()
}

Практический совет

Для коллекций сравнивайте правильно:

class Container(val items: List<String>) {
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other !is Container) return false
        return items == other.items // List'ы сравниваются правильно
    }
    override fun hashCode(): Int = items.hashCode()
}

Заключение

Переопределение equals() требует внимательности и понимания контракта. Главные правила: 1) всегда переопределять hashCode(); 2) проверять тип перед сравнением; 3) помнить о консистентности. Для простых случаев используйте data класс — Kotlin сделает всё за вас.

Как переопределять метод equals() | PrepBro