Переопределение метода javaquals() – не работает?
-
06-07-2019 - |
Вопрос
Я столкнулся с интересной (и очень неприятной) проблемой с equals()
Сегодняшний метод привел к сбою того, что я считал хорошо протестированным классом, и возникновению ошибки, на поиск которой мне потребовалось очень много времени.
Для полноты картины я не использовал IDE или отладчик — только старый добрый текстовый редактор и System.out.Время было очень ограничено, и это был школьный проект.
В любом случае -
Я разрабатывал базовую корзину для покупок, которая могла бы содержать ArrayList
из Book
объекты.Для того, чтобы реализовать addBook()
, removeBook()
, и hasBook()
методы Корзины, я хотел проверить, Book
уже существовал в Cart
.Итак, я ухожу -
public boolean equals(Book b) {
... // More code here - null checks
if (b.getID() == this.getID()) return true;
else return false;
}
В тестировании все работает нормально.Я создаю 6 объектов и заполняю их данными.Выполняйте множество операций добавления, удаления, 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
объект?Единственная разница заключалась в том, что он был создан из того же класса и заполнен только одним элементом данных.Я очень-очень смущен.Пожалуйста, пролейте свет?
Решение
В Java метод equals ()
, унаследованный от Object
, имеет вид:
public boolean equals(Object other);
Другими словами, параметр должен иметь тип Object
.
ArrayList
использует правильный метод equals, где вы всегда вызывали тот, который не корректно переопределял equals Object
.
Неправильная переопределение метода может вызвать проблемы.
Я переопределяю, равняется следующему:
@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
может помочь с глупыми ошибками.
Используйте его всякий раз, когда вы думаете, что переопределяете метод суперкласса или интерфейса. Таким образом, если вы сделаете это неправильно, вы получите ошибку компиляции.
Другие советы
Если вы используете Eclipse, просто перейдите в верхнее меню
Источник - > Создать равно () и хэш-код () р>
Немного не по теме, но, вероятно, стоит упомянуть в любом случае:
Commons Lang предлагает несколько превосходных методов, которые можно использовать для переопределения равенства и хэш-кода. Проверьте EqualsBuilder.reflectionEquals (...) и HashCodeBuilder.reflectionHashCode (...) . Это избавило меня от головной боли в прошлом - хотя, конечно, если вы просто хотите сделать «равно» на удостоверение личности это может не соответствовать вашим обстоятельствам.
Я также согласен с тем, что вы должны использовать аннотацию @Override
всякий раз, когда вы переопределяете equals (или любой другой метод).
Еще одним быстрым решением, которое сохраняет стандартный код, является аннотация Lombok EqualsAndHashCode . Это легко, элегантно и настраиваемо. И не зависит от 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;
См. параметры , чтобы указать, какие поля использовать в равных. Ломбок доступен в maven . Просто добавьте его с предоставленной областью действия:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.14.8</version>
<scope>provided</scope>
</dependency>
В Android Studio есть alt + INSERT ---> equals и Hashcode
Пример:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Proveedor proveedor = (Proveedor) o;
return getId() == proveedor.getId();
}
@Override
public int hashCode() {
return getId();
}
Учитывать:
Object obj = new Book();
obj.equals("hi");
// Oh noes! What happens now? Can't call it with a String that isn't a Book...
оператор instanceOf
часто используется в реализации equals.
Это популярная ловушка!
Проблема в том, что использование instanceOf
нарушает правило симметрии:
(object1.equals (object2) == true)
тогда и только тогда, когда (object2.equals (object1))
если первое значение равно true, а object2 является экземпляром подкласса класс, к которому принадлежит obj1, тогда второе равенство вернет false!
если рассматриваемый класс, к которому принадлежит ob1, объявлен как final, то этот Проблема не может возникнуть, но в целом вы должны проверить следующее:
this.getClass ()! = otherObject.getClass ();
, если нет, вернуть false, в противном случае проверить
поля для сравнения на равенство!
recordId является свойством объекта
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Nai_record other = (Nai_record) obj;
if (recordId == null) {
if (other.recordId != null)
return false;
} else if (!recordId.equals(other.recordId))
return false;
return true;
}