2008-10-09 3 views
143

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

Только для полноты я не использовал 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 объектов и заполняю их данными. У многих добавляет, удаляет, имеет() операции на 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 был правильно протестирован и исправлен. После swaapping метод equals() к следующему:

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

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

+0

Я aw что я нарушил «Контракт» относительно переопределения методов равенства, будучи рефлексивным, однако мне нужен был быстрый способ проверить, существовал ли объект в ArrayList без использования дженериков. – 2008-10-09 04:29:18

+0

Это хороший урок, чтобы узнать о Java и равных – jjnguy 2008-10-09 04:34:59

+5

Я не могу видеть, как корзина может быть равна книге. Я имею в виду, как может `hasBook (Book b)` вернуть результат `this.equals (b)`? – Robert 2012-07-14 00:52:12

ответ

304

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

public boolean equals(Object other); 

Другими словами, параметр должен быть типа Object.

В методе ArrayList используется правильный метод equals, где вы всегда вызывали тот, который не правильно переопределял Object.

Неправильное использование метода может вызвать проблемы.

I переопределение равен следующий каждый раз:

@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 аннотацию может помочь тонну с глупыми ошибками.

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

+29

Это хороший аргумент в пользу аннотации @Override ... если бы OP использовал @Override, его компилятор сказал бы ему, что он фактически не переопределяет метод родительского класса ... – Cowan 2008-10-09 04:36:28

11

Немного не по теме на ваш вопрос, но это, вероятно, стоит отметить, в любом случае:

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

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

95

Если вы используете затмение просто перейти к главному меню

Источник -> Генерация равных() и хэш-код()

4

Другое быстрое решение, которое экономит шаблонный код является Lombok EqualsAndHashCode annotation. Это легко, элегантно и настраиваемо. И не зависит от 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; 

Смотрите options Доступные для настройки поля для использования в равных. Ломбок доступен в maven. Просто добавьте его при условии объем:

<dependency> 
    <groupId>org.projectlombok</groupId> 
    <artifactId>lombok</artifactId> 
    <version>1.14.8</version> 
    <scope>provided</scope> 
</dependency> 
1

instanceOf утверждение часто используется при реализации равных.

Это популярная ловушка!

Проблема заключается в том, что использование instanceOf нарушает правила симметрии:

(object1.equals(object2) == true)тогда и только тогда, когда(object2.equals(object1))

если первый равен истинно, и object2 является экземпляром подкласса в класс, где принадлежит obj1, то второе равно будет возвращать false!

, если рассматривать класс, где ob1 принадлежит объявляется окончательным, то эта проблема не возникает, но в общем, вы должны проверить следующим образом:

this.getClass() != otherObject.getClass(); если нет, то возвращать ложь, в противном случае тест поля для сравнения для равенства!

0

в Android Studio является альт + вставить ---> равно и хэш-код

Пример:

@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(); 
} 
-1

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; 
    } 
1

Рассмотрим:

Object obj = new Book(); 
obj.equals("hi"); 
// Oh noes! What happens now? Can't call it with a String that isn't a Book... 
Смежные вопросы