Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Как equals() сравнивает объекты
equals() — это метод для сравнения содержимого двух объектов. По умолчанию он сравнивает ссылки (как ==), но можно переопределить для сравнения значений.
Стандартная реализация в Object
public boolean equals(Object obj) {
return this == obj; // Сравниваются ссылки, не содержимое
}
По умолчанию equals() работает как оператор == — сравнивает, указывают ли оба параметра на ОДНОТот же объект в памяти.
Пример: стандартное сравнение
class User {
val name: String
val age: Int
constructor(name: String, age: Int) {
this.name = name
this.age = age
}
}
val user1 = User("John", 30)
val user2 = User("John", 30)
println(user1 == user2) // false (разные объекты в памяти)
println(user1.equals(user2)) // false (стандартная реализация)
println(user1 === user2) // false (разные ссылки)
Переопределение equals() — сравнение значений
Чтобы сравнивать содержимое, нужно переопределить equals():
class User(
val name: String,
val age: Int
) {
override fun equals(other: Any?): Boolean {
// 1. Проверка типа
if (other !is User) return false
// 2. Сравнение полей
return name == other.name && age == other.age
}
override fun hashCode(): Int {
// ВАЖНО: если переопределил equals, переопредели и hashCode!
return name.hashCode() * 31 + age.hashCode()
}
}
val user1 = User("John", 30)
val user2 = User("John", 30)
println(user1 == user2) // true (одинаковые значения)
println(user1.equals(user2)) // true
println(user1 === user2) // false (разные объекты)
Data Class — автоматическое equals()
В Kotlin data class автоматически генерирует equals(), hashCode() и toString():
data class User(
val name: String,
val age: Int
)
val user1 = User("John", 30)
val user2 = User("John", 30)
println(user1 == user2) // true (сгенерирован equals)
println(user1.hashCode() == user2.hashCode()) // true
Таблица: == vs equals() vs ===
| Оператор | Что проверяет | Переопределяется |
|---|---|---|
| == | Вызывает equals() | Да (переопределяется в классе) |
| equals() | По умолчанию ссылки, можно переопределить | Да |
| === | Ссылки (идентичность) | Нет (всегда сравнивает адреса) |
Правила переопределения equals()
Эти правила обязательны (контракт equals/hashCode):
1. Рефлексивность: x.equals(x) должен быть true
val user = User("John", 30)
println(user.equals(user)) // true
2. Симметричность: если x.equals(y), то y.equals(x)
val user1 = User("John", 30)
val user2 = User("John", 30)
println(user1.equals(user2)) // true
println(user2.equals(user1)) // true (симметрично)
3. Транзитивность: если x.equals(y) и y.equals(z), то x.equals(z)
val user1 = User("John", 30)
val user2 = User("John", 30)
val user3 = User("John", 30)
println(user1.equals(user2) && user2.equals(user3)) // true
println(user1.equals(user3)) // true (транзитивно)
4. Согласованность с hashCode()
Если два объекта равны (equals() = true), их hashCode() ДОЛЖНЫ быть одинаковыми:
if (obj1.equals(obj2)) {
require(obj1.hashCode() == obj2.hashCode())
}
Правильная реализация equals()
class Person(
val name: String,
val age: Int,
val email: String
) {
override fun equals(other: Any?): Boolean {
// 1. Проверка null
if (other == null) return false
// 2. Проверка типа
if (other !is Person) return false
// 3. Сравнение всех значимых полей
return name == other.name &&
age == other.age &&
email == other.email
}
override fun hashCode(): Int {
// Используем все поля, что в equals
var result = name.hashCode()
result = 31 * result + age.hashCode()
result = 31 * result + email.hashCode()
return result
}
}
Ошибки при переопределении equals()
❌ Ошибка 1: забыл hashCode()
class User(val name: String) {
override fun equals(other: Any?): Boolean {
return other is User && name == other.name
}
// hashCode НЕ переопределён!
}
val map = mutableMapOf<User, String>()
val user1 = User("John")
val user2 = User("John")
map[user1] = "value1"
println(map[user2]) // null! (разные hashCode)
println(user1.equals(user2)) // true (но в HashMap по разным клавишам!)
❌ Ошибка 2: сравнение по ссылке вместо значений
class User(val name: String) {
override fun equals(other: Any?): Boolean {
return this === other // Ошибка! Работает как Object.equals
}
}
val user1 = User("John")
val user2 = User("John")
println(user1.equals(user2)) // false (разные ссылки)
❌ Ошибка 3: сравнение не всех полей
data class User(
val name: String,
val age: Int,
val password: String
) {
override fun equals(other: Any?): Boolean {
if (other !is User) return false
return name == other.name && age == other.age
// password забыли! Это нарушает контракт
}
}
equals() в коллекциях
equals() используется при поиске в коллекциях:
data class Product(val id: Int, val name: String)
val list = listOf(
Product(1, "Phone"),
Product(2, "Laptop"),
Product(3, "Tablet")
)
val search = Product(2, "Laptop")
println(search in list) // true (использует equals)
// Внутри используется цикл:
for (item in list) {
if (item.equals(search)) {
return true
}
}
equals() vs compareTo()
| Метод | Для чего | Возвращает |
|---|---|---|
| equals() | Равенство | Boolean |
| compareTo() | Упорядочение | -1 (меньше), 0 (равно), 1 (больше) |
data class Version(val major: Int, val minor: Int) : Comparable<Version> {
override fun equals(other: Any?): Boolean {
return other is Version && major == other.major && minor == other.minor
}
override fun compareTo(other: Version): Int {
return when {
major != other.major -> major.compareTo(other.major)
else -> minor.compareTo(other.minor)
}
}
}
val v1 = Version(1, 2)
val v2 = Version(1, 2)
val v3 = Version(1, 3)
println(v1 == v2) // true (equals)
println(v1 < v3) // true (compareTo)
println(v1.compareTo(v2)) // 0 (равны)
Итог
- equals() по умолчанию сравнивает ссылки (как ===)
- equals() можно переопределить для сравнения значений
- При переопределении equals() ОБЯЗАТЕЛЬНО переопредели hashCode()
- data class автоматически генерирует equals() и hashCode()
- == вызывает equals(), а === сравнивает ссылки
- Правила: рефлексивность, симметричность, транзитивность, согласованность с hashCode()