Есть ли в Kotlin аналог equals и hashCode и как это реализовано
Что проверяют
Знаете ли вы, как в Kotlin устроено сравнение: что делает ==, чем он отличается от === и что именно генерирует data class. Следом спрашивают, какие свойства попадают в equals и почему data class опасно делать JPA-сущностью.
Как отвечать
Аналог есть, и это те же методы JVM. У Any объявлены equals(other: Any?), hashCode() и toString(), их переопределяют через override. Контракт тот же, что в Java: равные объекты обязаны давать одинаковый хэш.
Отличия в синтаксисе и генерации:
==: структурное равенство, компилируется вa?.equals(b) ?: (b === null), поэтому безопасно дляnull. Это противоположность Java, где==для объектов сравнивает ссылки;===: ссылочное равенство, аналог==в Java;data class: компилятор генерируетequals,hashCode,toString,copyиcomponentNпо свойствам первичного конструктора. Свойства, объявленные в теле класса, в сравнение не входят;- массивы: свойство типа
ArrayилиIntArrayвdata classсравнивается по ссылке, IDE предупреждает об этом и предлагает переопределитьequalsчерезcontentEquals; - ручная реализация: в обычном классе IntelliJ IDEA генерирует пару методов сама, хэш собирают через
Objects.hash(...)или по шаблону31 * result + ....
Хорошо упомянуть и наследование: data class не может быть open, а equals в иерархии классов легко нарушает симметрию, поэтому для сравнения по значению предпочитают финальные классы.
Где спотыкаются
Говорят, что хэш-код «уникален». Он не обязан быть уникальным, требование одно: у равных объектов хэши равны. Коллизии для HashMap нормальная ситуация.
Делают JPA-сущность data class. Сгенерированный equals по всем полям меняется после присвоения id, трогает ленивые связи и ломает поведение в HashSet. Для сущности берут обычный класс с equals по идентификатору.
Кладут в HashSet объект data class с var-свойствами, а потом их меняют. Хэш меняется, и элемент в наборе уже не найти, хотя он там лежит.