Вопрос-ответ

Overriding the java equals() method - not working?

Переопределение метода java equals() - не работает?

Сегодня я столкнулся с интересной (и очень неприятной) проблемой с equals() методом, которая привела к сбою того, что я считал хорошо протестированным классом, и вызвала ошибку, на обнаружение которой у меня ушло очень много времени.

Просто для полноты картины я не использовал IDE или отладчик - просто старый добрый текстовый редактор и System.out. Время было очень ограничено, и это был школьный проект.

В любом случае -

Я разрабатывал базовую корзину для покупок, которая могла содержать ArrayList несколько Book объектов. Чтобы реализовать addBook(), removeBook() и hasBook() методы Cart, я хотел проверить, Book уже существует ли в Cart. Итак, я ухожу -

public boolean equals(Book b) {
... // More code here - null checks
if (b.getID() == this.getID()) return true;
else return false;
}

В тестировании все работает нормально. Я создаю 6 объектов и заполняю их данными. Выполняю множество операций add, removes, has() над Cart и все работает нормально. Я читал, что вы можете либо иметь equals(TYPE var), либо equals(Object o) { (CAST) var } но предположил, что, поскольку это работает, это не имеет особого значения.

Затем я столкнулся с проблемой - мне нужно было создать Book объект, в котором есть только ID из класса Book. Никакие другие данные в него вводиться не будут. В основном следующее:

public boolean hasBook(int i) {
Book b = new Book(i);
return hasBook(b);
}

public boolean hasBook(Book b) {
// .. more code here
return this.books.contains(b);
}

Внезапно equals(Book b) метод больше не работает. Потребовалось ОЧЕНЬ много времени, чтобы отследить это без хорошего отладчика и предполагая, что Cart класс был должным образом протестирован и корректен. После замены equals() метода на следующий:

public boolean equals(Object o) {
Book b = (Book) o;
... // The rest goes here
}

Все снова заработало. Есть ли причина, по которой метод решил не принимать параметр Book, хотя он явно был Book объектом? Единственное отличие, казалось, заключалось в том, что он был создан из того же класса и заполнен только одним элементом данных. Я очень, очень смущен. Пожалуйста, пролейте немного света?

Переведено автоматически
Ответ 1

В Java equals() метод, который наследуется от Object, является:

public boolean equals(Object other);

Другими словами, параметр должен иметь тип Object. Это называется переопределением; ваш метод public boolean equals(Book other) выполняет то, что называется перегрузкой для equals() метода.

В ArrayList использует переопределенные equals() методы для сравнения содержимого (например, для его contains() и equals() методов), не перегруженных. В большей части вашего кода вызов метода, который не переопределял должным образом Object метод equals, был хорош, но несовместим с ArrayList.

Таким образом, неправильное переопределение метода может вызвать проблемы.

Я каждый раз переопределяю equals следующим образом:

@Override
public boolean equals(Object other){
if (other == null) return false;
if (other == this) return true;
if (!(other instanceof MyClass)) return false;
MyClass otherMyClass = (MyClass)other;
...test other properties here...
}

Использование @Override аннотации может сильно помочь при устранении глупых ошибок.

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

Ответ 2

Если вы используете eclipse, просто перейдите в верхнее меню


Источник --> Сгенерировать equals() и hashCode()


Ответ 3

Немного не по теме вашего вопроса, но, вероятно, его все равно стоит упомянуть:

ВCommons Lang есть несколько отличных методов, которые вы можете использовать для переопределения equals и hashcode. Проверьте EqualsBuilder.reflectionEquals(...) и HashCodeBuilder.reflectionHashCode(...). В прошлом я избавился от множества проблем с головной болью - хотя, конечно, если вы просто хотите сделать "equals" для ID, это может не соответствовать вашим обстоятельствам.

Я также согласен с тем, что вы должны использовать @Override аннотацию всякий раз, когда вы переопределяете equals (или любой другой метод).

Ответ 4

Еще одно быстрое решение, позволяющее сохранить шаблонный код, - это аннотации Lombok Equals и hashCode. Это простое, элегантное и настраиваемое решение. И не зависит от IDE. Например;

import lombok.EqualsAndHashCode;

@EqualsAndHashCode(of={"errorNumber","messageCode"}) // Will only use this fields to generate equals.
public class ErrorMessage{

private long errorNumber;
private int numberOfParameters;
private Level loggingLevel;
private String messageCode;

Смотрите доступные опции, чтобы настроить, какие поля использовать в equals. Lombok доступен в maven. Просто добавьте его с предоставленной областью видимости:

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.14.8</version>
<scope>provided</scope>
</dependency>
2023-03-18 20:47 java