Существует два способа сравнить объекты в языке Java:
- Оператор ==
- Оператор equals()
Сравнить объекты оператором equals() можно следующим образом:
a.equals(b);
Метод equals() принадлежит типу Object , который является предком каждого класса в языке Java. Это означает, что любой созданный вами класс будет наследовать базовое поведение equals() от Object . Это базовое поведение не отличается от оператора == . Другими словами, по умолчанию эти два выражения используют оператор == и возвращают значение false :
a == b; a.equals(b);
Посмотрите на метод spendMoney() класса Adult снова. Что происходит за кулисами, когда мы вызываем метод contains() нашей переменной wallet ? Язык Java использует оператор == для сравнения объектов в списке с указанным объектом. Если он находит соответствие, метод возвращает значение true ; в противном случае возвращается false . Поскольку мы сравниваем примитивы, он может найти соответствие, основанное на целочисленных значениях (помните, что оператор == сравнивает примитивы по их значению).
Это хорошо для примитивов, но что если мы хотим сравнить содержимое объектов? Оператор == не сделает это. Для сравнения содержимого объектов мы должны перегрузить метод equals() класса, экземпляром которого является переменная a . Это означает, что нужно создать метод с точно такой же сигнатурой, что и у метода одного из ваших суперклассов, но реализовать метод по другому. Если сделать это, вы сможете сравнивать содержимое двух объектов, а не только проверять, ссылаются ли две переменные на один и тот же экземпляр.
Попробуйте ввести следующий код в метод main() и взгляните на результат, отображаемый в консоли:
Adult adult1 = new Adult(); Adult adult2 = new Adult(); System.out.println(adult1 == adult2); System.out.println(adult1.equals(adult2)); Integer integer1 = new Integer(1); Integer integer2 = new Integer(1); System.out.println(integer1 == integer2); System.out.println(integer1.equals(integer2));
Первое сравнение возвращает значение false , поскольку adult1 и adult2 ссылаются на разные экземпляры Adult . Второе сравнение тоже возвращает значение false , поскольку реализация метода equals() по умолчанию просто проверяет, ссылаются ли обе переменные на один и тот же экземпляр. Но поведение equals() по умолчанию обычно не то, что мы хотим. Мы хотели бы сравнивать содержимое двух объектов Adult , чтобы узнать, одинаковые ли они. Для этого мы можем перегрузить equals() . Как видно из двух последних сравнений в приведенном выше примере, класс Integer перегружает метод так, что оператор == возвращает false , но оператор equals() сравнивает на равенство упакованные значения int . Мы сделаем что-то похожее для класса Adult в следующем разделе.
Перегрузка equals()
Перегрузка метода equals() для сравнения объектов на самом деле требует от нас перегрузки двух методов:
public boolean equals(Object other) { if (this == other) return true; if ( !(other instanceof Adult) ) return false; Adult otherAdult = (Adult)other; if (this.getAge() == otherAdult.getAge() && this.getName().equals(otherAdult.getName()) && this.getRace().equals(otherAdult.getRace()) && this.getGender().equals(otherAdult.getGender()) && this.getProgress() == otherAdult.getProgress() && this.getWallet().equals(otherAdult.getWallet())) return true; else return false; } public int hashCode() { return firstname.hashCode() + lastname.hashCode(); }
Мы перегрузим метод equals() следующим способом, который является обычным стилем в Java:
- Если объект, который мы собираемся сравнивать, является самим этим объектом, то они очевидно равны, поэтому возвращаем true .
- Проверяем для уверенности, что объект, который мы собираемся сравнивать, является экземпляром Adult (если нет, два объекта очевидно не одинаковы).
- Приводим тип входного объекта к Adult , для того чтобы использовать его методы.
- Сравниваем части двух объектов Adult , которые должны быть равны, для того чтобы два объекта считались «равными» (какое бы определение равенства мы ни использовали).
- Если какие-либо из этих частей не равны, возвращаем значение false ; в противном случае возвращаем true .
Обратите внимание на то, что мы можем сравнить возраст каждого объекта с помощью == , поскольку это примитивные значения. Для сравнения String мы используем equals() , поскольку этот класс перегружает метод equals() для сравнения содержимого объектов String (если бы мы использовали == , то получали бы false каждый раз, потому что два объекта String никогда не будут одним и тем же объектом). То же самое мы делаем и для ArrayList , поскольку он перегружает equals() для проверки того, что два списка содержат одинаковые элементы в одинаковом порядке. Это хорошо подходит для нашего простого примера.
Когда бы вы ни перегрузили equals() , необходимо также перегрузить hashCode() . Объяснение причины этого выходит за рамки данного руководства, а пока просто знайте, что язык Java использует значение, возвращенное из этого метода, для помещения экземпляров вашего класса в коллекции, которые используют хэш-алгоритм размещения объектов (например HashMap ). Единственными практическими правилами для этого метода (кроме того, что он должен возвращать целое значение) являются два приведенные далее правила. Метод hashCode() должен возвращать:
- Одно и то же значение для одного и того же объекта постоянно.
- Одинаковые значения для одинаковых объектов.
Как правило, возврат значений хэш-кода для некоторых или всех переменных экземпляра объекта является достаточно хорошим способом вычисления хэш-кода. Другим вариантом является преобразование переменных в объекты String , объединение их и возврат хэш-кода для полученного объекта String . Еще одним вариантом является умножение одной или нескольких числовых переменных на некоторую константу для дальнейшего обеспечения уникальности, но это часто излишне.