На этом шаге мы рассмотрим создание в среде функций equals() и hashCode().
Представим класс Weapon, который обладает свойствами name и type:
open class Weapon(val name: String, val type: String)
Предположим, вы хотите, чтобы оператор равенства (==) считал два разных экземпляра оружия структурно эквивалентными, если значения их свойств name и type структурно равны. По умолчанию, как было сказано ранее, оператор == проверяет равенство ссылок, поэтому следующее выражение вернет false:
open class Weapon(val name: String, val type: String) println(Weapon("ebony kris", "dagger") == Weapon("ebony kris", "dagger")) // False
Напредыдущих шагах вы узнали, что классы данных могут решить эту проблему. Для этого нужна реализация equals(), которая сравнивает все свойства, объявленные в главном конструкторе. Но Weapon не может быть (и не будет) классом данных, потому что он является базой для видов оружия (модификатор open). Классам данных запрещено быть суперклассами.
Однако на 188 шаге "Перегрузка операторов" мы видели, что можно реализовать свои версии equals() и hashCode() для структурного сравнения экземпляров класса.
Это распространенная задача, поэтому в IntelliJ есть команда Generate, добавляющая переопределения функций, доступная в меню как Code | Generate. Если выбрать эту команду, высветится диалоговое окно (рисунок 1).
Рис.1. Диалоговое окно Generate
Когда переопределяются функции equals() и hashCode(), дается возможность выбрать свойства для структурного сравнения двух экземпляров вашего объекта (рисунок 2).
Рис.2. Генерация переопределенных equals() и hashCode()
InteliJ добавит функции equals() и hashCode() в класс, основываясь на вашем выборе:
open class Weapon(val name: String, val type: String) { override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false other as Weapon if (name != other.name) return false if (type != other.type) return false return true } override fun hashCode(): Int { var result = name.hashCode() result = 31 * result + type.hashCode() return result } }
Рис.3. Добавленные функции
Теперь, с этими переопределенными функциями, сравнение двух видов оружия вернет true, если они будут иметь одинаковые значения в свойствах name и type:
println(Weapon("ebony kris", "dagger") == Weapon("ebony kris", "dagger")) // True
Обратите внимание на то, как сгенерированная переопределенная функция equals() определяет структурное равенство свойств, выбранных в диалоге Generate:
. . . . if (name != other.name) return false if (type != other.type) return false . . . .
Если какие-либо свойства структурно не эквивалентны, то сравнение вернет результат false, в противном случае - true.
Как уже упоминалось ранее, переопределяя функцию equals(), также желательно переопределить функцию hashCode(). Функция hashCode() улучшает производительность, то есть скорость извлечения значения по ключу при использовании ассоциативного массива, например, и это имеет прямое отношение к уникальности экземпляра класса.
На следующем шаге мы рассмотрим алгебраические типы данных.