Метод equals в java — реализация на примерах

Фундаментальным аспектом любого класса Java является его определение равенства. Он определяется методом равных классов, и для правильной реализации есть несколько вещей. Давайте определим их, чтобы правильно понимать метод equals в java.

Метод определяет, является ли объект Number, вызывающий метод, равным объекту, который передается в качестве аргумента.

Синтаксис:

public boolean equals(Object o)

Метод возвращает значение True, если аргумент не равен null и является объектом того же типа и с тем же числовым значением.

Существуют некоторые дополнительные требования для объектов Double и Float, которые описаны в документации API Java.

Обратите внимание, что реализация equals всегда означает, что hashCode также должен быть реализован!

метод equal

Идентичность в сравнении с равенством в Java

Посмотрите на этот фрагмент кода:

String some = "some string";
String other = "other string";

У нас две строки и они разные.

Теперь разберем эти:
String some = "some string";
String other = some;
boolean identical = some == other;

Здесь мы имеем только один экземпляр String, some и other ссылаются на него. В Java some и other идентичны и, соответственно, identical есть true.

Теперь посмотрите на такой пример:
String some = "some string";
String other = "some string";
boolean identical = some == other;

Теперь some и other указывают на разные экземпляры и больше не идентичны, поэтому identical является ложным.

В терминах Java они равны, что проверяется с помощью equals:
String some = "some string";
String other = "some string";
boolean equal = some.equals(other);

Здесь equals есть true.

Идентификатор переменной (также называемый ссылочным равенством) определяется ссылкой, которую он содержит. Если две переменные содержат одну и ту же ссылку, они идентичны. Это проверяется с помощью ==.

Равенство переменной определяется значением, на которое оно ссылается. Если две переменные ссылаются на одно и то же значение, то они равны. Это проверяется с помощью equals.

Но что означает «то же значение»? По сути, именно реализация равных определяет «одинаковость». Метод equals определен в Object, и так как все классы наследуются от него, у всех есть этот метод.

Реализация в Object проверяет identity (обратите внимание, что одинаковые переменные также равны), но многие классы переопределяют его чем-то более подходящим. Для строк, например, он сравнивает последовательность символов, а для дат он гарантирует, что оба указывают на один и тот же день.

Когда же стоит делать перегрузку метода equals? Это стоит делать только тогда, когда для вашего класса определено понятие логической эквивалентности, которая не совпадает с тождественностью объектов.

Например, для классов Integer и String данное понятие можно применить.

Переопределение метода equals позволяет использовать экземпляры класса в качестве ключей в некой схеме или элементов в неком наборе, имеющих необходимое и предсказуемое поведение.

переопределение equals

Многие структуры данных используют Equals для проверки того, содержат ли они элемент.
Например:

List list = Arrays.asList("a", "b", "c");
boolean contains = list.contains("b");

Переменная contains имеет значение true, хотя «b» не идентичны, но они равны. (Здесь начинает работать hashCode.)
Для лучшего понимания работы equals в java рассмотрим такой пример. Допустим, мы сравниваем ноутбуки и считаем их равными, если они имеют одинаковые характеристики.

Одно свойство настолько тривиально, что его вряд ли стоит упоминать: каждая вещь равна самой себе.
Если есть 2 объекта: если одна вещь равна другой, другая также равна первой. Очевидно, если мой ноутбук равен вашему, ваш равен моему.

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

equals-это не что иное, как формализация того, что мы видели выше.

Метод equals реализует отношение эквивалентности для ненулевых ссылок на объекты:

  1. Рефлексивно: для любого ненулевого ссылочного значения x, x.equals (x) должен возвращать true.
  2. Симметричено: для любых ненулевых ссылочных значений x и y, x.equals (y) должен возвращать true тогда и только тогда, когда y.equals (x) возвращает true.
  3. Транзитивно: для любых ненулевых ссылочных значений X, Y и Z, если x.equals(Y) возвращает True и Y.equals(Z) возвращает True, то x.equals(Z) должен возвращать True.
  4. Непротиворечиво: для любых ненулевых значений x и y множественные вызовы x.equals (y) последовательно возвращается true или последовательно возвращается false при условии, что информация, используемая в сравнениях equals для объектов не изменяется.
  5. Для любого ненулевого значения x, x.equals (null) должен возвращать false.

Реализация метода equals

Для класса Person со строковыми полями firstName и lastName общий вариант для реализации equals:

@Override
public boolean equals(Object o) {
// self check
if (this == o)
return true;
// null check
if (o == null)
return false;
// type check and cast
if (getClass() != o.getClass())
return false;
Person person = (Person) o;
// field comparison
return Objects.equals(firstName, person.firstName)
&& Objects.equals(lastName, person.lastName);
}

Очень важно, что equals принимает Object! В противном случае возникает непредвиденное.

Например, предположим, что мы будем реализовывать equals (Person) так:
public boolean equals(Person person) {
return Objects.equals(firstName, person.firstName)
&& Objects.equals(lastName, person.lastName);
}

Посмотрим на простом примере:

Person elliot = new Person("Elliot", "Alderson");
Person mrRobot = new Person("Elliot", "Alderson");
boolean equal = elliot.equals(mrRobot);

equal принимает значение true, а теперь:

Person elliot = new Person("Elliot", "Alderson");
Object mrRobot = new Person("Elliot", "Alderson");
boolean equal = elliot.equals(mrRobot);

Теперь это ложь. Может, не совсем то, что мы ожидали.

Причина в том, что Java вызывает Person.equals(Object) (наследуется от объекта, который проверяет идентичность). Почему?

Стратегия Java в выборе метода основана не на типе среды выполнения параметров, а на его объявленном типе.Поэтому, если Mr Robot объявлен как объект, Java вызывает Person.equals(Object) вместо нашего Person.equals(Person).

hashcode java

Самопроверка

Равенство является фундаментальным свойством любого класса, и оно может в конечном итоге вызываться очень часто, например, в циклах. Таким образом, его производительность имеет значение! И самоконтроль в начале нашей реализации — это просто оптимизация производительности.

java if (this == o) возвращает true;

Может показаться, что он должен реализовывать рефлексивность, но проверки дальше были бы очень странными, если бы они не делали этого.

Проверка NULL значения.
Ни один экземпляр не должен быть равен null, поэтому здесь мы убедимся в этом. В то же время он защищает код от Nullpointerexception.
if (o == null)
return false;

Фактически он может быть включен в следующую проверку, например:
if (o == null || getClass() != o.getClass())
return false;

Заключение

Мы выяснили разницу между идентичностью (должна быть одна и та же ссылка, проверена с помощью ==) и равенством (могут быть разные ссылки на «одно и то же значение»).

  • Обязательно переопределите equals (Object), чтобы наш метод всегда вызывался.
  • Включите проверку self и null.
  • Используйте getClass, чтобы позволить подтипам выполнять свою собственную реализацию (но не сравнивать по подтипам) или использовать instanceof и make
  • equals final (и подтипы могут быть равны).
  • Сравните нужные поля с помощью Objects.equals.
  • Или пусть ваша IDE генерирует все для вас и редактирует там, где это необходимо.

Автор Cascading Style Sheets