Есть ли в Kotlin аналог equals и hashCode и как это реализовано
Что проверяют
Знаете ли вы, как в Kotlin устроено сравнение объектов, и понимаете ли, как это влияет на ваши автотесты. Для тестировщика вопрос прикладной: assertEquals и shouldBe работают через equals, а DTO из ответов API почти всегда описаны как data class. Следом спрашивают про контракт equals и hashCode или про разницу == и ===.
Как отвечать
Отдельного «аналога» нет, это те же методы: в Kotlin они объявлены у Any, корня всех классов, и переопределяются через override.
==: структурное сравнение, вызываетequalsи безопасно работает сnull. В отличие от Java, строки и объекты сравнивают именно так;===: сравнение ссылок, тот же ли это объект;data class: компилятор генерируетequals,hashCode,toString,copyиcomponentN, но только по свойствам из первичного конструктора. Свойство, объявленное в теле класса, в сравнении не участвует;- контракт: равные объекты обязаны иметь равный
hashCode, иначеHashSetиHashMapначинают терять элементы. Поэтому методы переопределяют только парой; - массивы:
Arrayвнутриdata classсравнивается по ссылке, нужныcontentEqualsиcontentHashCode.Listсравнивается по содержимому.
Как это выглядит в тестах: DTO ответа описываете как data class, и assertEquals(expected, actual) сравнивает поля, а при падении сгенерированный toString показывает понятную разницу. Если в ответе есть поля, которые меняются от запуска к запуску (id, createdAt), либо сравнивайте с expected.copy(id = actual.id), либо используйте рекурсивное сравнение с исключением полей, например usingRecursiveComparison().ignoringFields(...) в AssertJ.
Где спотыкаются
Переносят привычку из Java и считают, что == в Kotlin сравнивает ссылки. Отсюда лишние вызовы equals() в коде и путаница в объяснении.
Не знают, что data class учитывает только первичный конструктор. Тест проходит, хотя поле в теле класса у объектов разное.
Кладут data class с var-свойствами в HashSet, а потом меняют поле. Хэш меняется, и элемент уже не находится через contains, хотя физически лежит в множестве.