Метод equals в Java
Метод equals() отвечает за сравнение строки с определенным объектом. Значение true будет результатом только в том случае, если аргумент не равен null и представляет собой строковый объект (String),который представляет такую же последовательность символов, как и данный объект.
В своей работе программистам часто приходится проверять равенство объектов. Рассмотрим, как работает сравнение на языке Java.
Сравнение с помощью == и equals в Java
Пример кода:
String s1 = new String(«vscode.ru»);
String s2 = new String(«vscode.ru»);System.out.println(s1 == s2); //каким здесь будет результат сравнения?
System.out.println(s1.equals(s2)); //а здесь?
Какая разница между этими записями? Чем отличается оператор сравнения == и метод equals?
На самом деле все легко. При сравнении метод equals проверяет и сопоставляет значения объектов и на основе этого приходит к выводу раны они или нет (true или false).
Оператор == сравнивание значение переменных (в случае с примитивными типами данных) и возвращает результат. Однако в случае со ссылочными типами данных (массивы, объекты и так далее) сравнивает ссылки на объекты в памяти ПК, и на основе анализа выдает результат true или false. Вот и все отличие метода метода equals и оператора == в Java.
Метод equals представляет собой метод класса Object. При этом каждый объект неявно унаследован от класса Object и способен вызывать метод equals. Рассмотрим пример, который мы привели выше. Можно сделать вывод о результате операции сравнения в двух случаях:
- System.out.println(s1 == s2); //возвращено false. Сравниваются ссылки, а они отличаются.
- System.out.println(s1.equals(s2)); //возвращено true, так как значения объектов равны (они одинаковые).
Важно! Вместо класса String, может объявляться любой другой ссылочный тип данных (ваш пользовательский класс, ArrayList<>, Object и так далее).
Переопределение метода equals в Java
В каких случаях нужно предопределять метод equals? Это необходимо делать тогда, когда определено понятие логической эквивалентности для вашего класса. При этом эквивалентность не совпадает с тождественностью объектов. К примеру, это понятие можно применять для классов String и Integer.
Переопределение метода equals требуется не только, чтобы удовлетворить ожидания программистов. С его помощью можно использовать экзепляры класса как ключи в определенной схеме или элементов в некотором наборе с необходимым и предсказуемым поведением. Что это означает?
Приведем пример кода:
public class Address {
private Integer number;
private String street;public Address(Integer number, String street) {
this.number = number;
this.street = street;
}public Integer getNumber() {
return number;
}public String getStreet() {
return street;
}public static void main(String[] args) {
List<Address> addressList = new ArrayList<>();
addressList.add(new Address(1, «Test street»));
addressList.add(new Address(1, «Test street»));
while (addressList.remove(new Address(1, «Test street»)));
System.out.println(addressList.size());
}
}
В примере мы создаем список объектов Address, куда добавляем объект класса Address с идентичным конструктором. Данный объект добавляем два раза в List, и с помощью цикла избавляемся от него. Дальше выводим длину списка в консоль.
В результате в консоли, после выполнения программы, отобразится число 2. Возникает вопрос: «Почему кол-во записей в списке осталось неизменным? Ведь наша задача — их удалить». В рассматриваемом примере объекты с одинаковыми полями number и street, но они не удалились.
Во время удаления объектов из списка неявно вызывается метод equals и если объект, передаваемый для удаления, есть в списке, то он удаляется. Использование метода equals в классе Object подразумевает проверку только равенства ссылок. У экзампляторов разные ссылки, так как это различные объекты, каждый из них был разработан с помощью оператора new. Как решить эту проблему?
Ответ очевиден – нужно предопределить метод equals в классе Address:
@Override
public boolean equals(Object o) {
if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;
Address address = (Address) o;
if (number != null ? !number.equals(address.number) : address.number != null)
return false;if (street != null ? !street.equals(address.street) : address.street != null)
return false;return true;
}
После выполнения программы вы увидите, что кол-во объектов в списке теперь равно нулю.
Правила переопределения метода equals в Java
Существует пять правил переопределения метода equals в Java:
- Симметричность: для всех нулевых объектов х и у выражение x.equals(y) значение true должно возвращаться только тогда, когда у equals(x) возвратит true.
- Рефлексивность: для каждого ненулевого объекта х выражение x.equals(x) должно возвращать логическое значение true.
- Транзитивность: для 3-х ненулевых объектов х, у и z, если х.equals(y) возвращает логическое true и у.equals(z) возвращает true, то и выражение х.equals(z) должно возвращать true.
- Непротиворечивость: если несколько раз вызвать х.equals(у) для всех ненулевых объектов, то всегда должно возвращаться значение false или true, за исключением того, если никакая информация в объектах не сменилась.
- Для всех ненулевых х выражение х.equals(null) должно возвращать логическое значение false.
Для тех, кто не силен в математике, все это выглядит довольно странно. Но попробуйте разобраться в этом, внимательно перечитав все несколько раз. На самом деле здесь нет ничего сложного. Эти правила нарушать нельзя, иначе приложение будет работать неустойчиво и часто завершаться ошибкой. При этом выяснить источник ошибки данного класса будет непросто.