Java. Сравнение объектов с оператором equals()

Существует два способа сравнить объекты в языке Java:

Сравнить объекты оператором 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 . Еще одним вариантом является умножение одной или нескольких числовых переменных на некоторую константу для дальнейшего обеспечения уникальности, но это часто излишне.

Рейтинг
( Пока оценок нет )
Загрузка ...