Найти в Дзене

Метод equals() класса данных

Оглавление

1. Обзор

Как мы знаем, в Kotlin есть классы данных для хранения данных.

В этом уроке мы обсудим метод equals() класса данных через пару распространенных ошибок.

2. Класс данных Kotlin в двух словах

Класс данных Kotlin предназначен для хранения данных, например:

-2

Класс данных должен иметь непустой первичный конструктор. Кроме того, классы данных не могут быть унаследованы.

Класс данных Kotlin
предварительно реализует набор часто используемых методов, таких как getters/setters, copy(), toString(), hashcode() и equals(). Эти методы позволяют нам довольно легко манипулировать данными.

Однако, если мы плохо знаем метод equals() класса данных, у нас могут возникнуть проблемы.

Далее давайте лучше разберемся с методом equals() через две распространенные ловушки.

Для простоты мы будем использовать утверждения модульного теста для проверки результатов в этом руководстве.

3. Ошибка равенства классов данных №1

Во-первых, давайте немного расширим наш класс Person. Допустим, мы хотим ввести новое свойство dateOfBirth и превратить класс Person в класс PersonV1:

-3

Далее, давайте представим первую ловушку на примере.

3.1. Знакомство с ловушкой

Пример может показать проблему быстро. Допустим, мы создаем два экземпляра PersonV1:

-4

Как мы узнали, этот класс данных уже предоставляет методы hashcode() и equals(). Если мы сравним p1 и p2 с помощью метода equals(), несмотря на то, что у них одинаковые имена и фамилии, мы подумаем, что они явно не равны, поскольку их значения dateOfBirth разные.

Однако, если мы запустим приведенный ниже тест, он пройдет:

-5

Итак, этот результат говорит нам, что p1 и p2 на самом деле равны. Мы знаем, что, в отличие от Java, оператор Kotlin ‘==’ проверяет структурное равенство. В этом случае он вызывает метод equals() класса данных и выполняет сравнение значений.

Если бы мы думали, что p1 и p2 не равны, это могло бы привести к некоторым проблемам. Например, мы можем использовать их в качестве ключей для хранения некоторых данных в хэш-карте и ожидать, что будет две записи. Однако последняя запись перезапишет существующую из-за того, что p1 == p2.

Далее давайте разберемся, почему Kotlin сообщает нам p1 == p2, хотя их свойства dateOfBirth имеют разные значения.

3.2. Понимание метода equals() класса данных и решение проблемы

Вот почему класс данных p1 == p2: автоматически генерирует такие удобные методы, как equals(), hashcode(), toString() и copy(), только для свойств, объявленных в первичном конструкторе.

Другими словами, когда мы проверяем p1 == p2, сравниваются только свойства в первичном конструкторе. Свойство dateOfBirth отсутствует в основном конструкторе. Поэтому вообще не с чем сравнивать.

Теперь, когда мы понимаем, как работает метод equals() по умолчанию для классов данных, решение проблемы не будет для нас сложной задачей.

Итак, есть два пути. Первый вариант, который также является рекомендуемым подходом, заключается в
рефакторинге связанного кода и перемещении свойства dateOfBirth в первичный конструктор. Таким образом, все три свойства будут участвовать в проверке equals().

Кроме того, мы можем переопределить методы hashcode() и equals(), чтобы при вызове метода equals() для этого класса данных вызывался наш собственный реализованный метод equals().

Итак, теперь давайте создадим новый класс PersonV2 для переопределения hashcode() и equals():

-6

Далее давайте проверим, работает ли метод equals() должным образом:

-7

В тесте мы создали два экземпляра PersonV2 с одинаковыми именами и фамилиями, но их значения dateOfBirth разные. Так что на этот раз мы ожидаем, что они не равны.

4. Ошибка равенства классов данных №2

Мы узнали, что класс данных Kotlin проверяет свойства, объявленные в его основном конструкторе, только при выполнении проверки equals(). Итак, давайте посмотрим на другой пример класса данных:

-8

Как видно из приведенного выше кода, на этот раз все свойства объявлены в основном конструкторе BaeldungString. Итак, теперь давайте создадим несколько экземпляров и посмотрим, даст ли проверка equals() ожидаемый результат.

4.1. Знакомство с ловушкой

Опять же, давайте создадим два экземпляра BaeldungString:

-9

Как мы видели, хотя s1 и s2 являются разными объектами, они имеют одинаковые значения в значении String и свойствах символов CharArray. Таким образом, мы можем полагать, что s1 == s2 должно возвращать true.

Удивительно, но если мы выполним приведенный ниже тест, он пройдет.
То есть s1 не равно s2.

-10

Далее давайте разберемся, почему мы получаем такой результат и исправим проблему.

4.2. Понимание Array.equals() в Kotlin и решение проблемы

Когда мы вызываем метод equal() класса данных, Kotlin сравнивает свойства, объявленные в основном конструкторе. Кроме того, когда Kotlin сравнивает свойства, он вызывает метод equals() типа свойства.

Например, для s1 == s2 Kotlin проверяет s1.value == s2.value и s1.chars == s2.chars. Как мы уже упоминали, оператор == проверяет структурное равенство.

Но почему s1 == s2 возвращает false? Это потому, что,
в отличие от List, array1 == array2 сравнивает их ссылки в Kotlin.

Давайте создадим тест, чтобы увидеть разницу:

-11

Приведенный выше тест проходит, если мы запустим его.

Следовательно, == — неправильный способ сравнения значений двух массивов.
Если мы хотим проверить структурное равенство массива, мы должны использовать метод массива contentEquals():

-12

Приведенное выше утверждение проходит.

Теперь давайте исправим проблему.

Поскольку оператор List '==' проверяет значения, наиболее простым решением является замена массива в классе данных списком. Далее, вообще говоря,
мы должны предпочесть списки массивам.

Если по какой-то причине мы должны использовать тип массива в классе данных, мы можем решить проблему, переопределив методы equals() и hashcode(). Но следует отметить, что
в методе equals() мы должны использовать метод contentEquals() вместо ‘==‘ для сравнения значений массивов.

Давайте создадим BaeldungStringV2 и переопределим эти два метода:

-13

Наконец, давайте создадим два объекта BaeldungStringV2 с одинаковыми значениями и проверим, получим ли мы ожидаемый результат:

-14

Тест проходит, когда мы его выполняем. Итак, проблема решена.

5. Вывод

В этой статье мы обсудили метод equals() класса данных Kotlin через несколько распространенных ошибок.

Оригинал статьи: https://www.baeldung.com/kotlin/data-class-equals-method

Наука
7 млн интересуются