Переопределение Java-метода equals () - не работает? - PullRequest
147 голосов
/ 09 октября 2008

Сегодня я столкнулся с интересной (и очень расстраивающей) проблемой с методом 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, хотя он явно был a Book объектом? Казалось, единственное отличие было в том, что он был создан из одного и того же класса и заполнен только одним элементом данных. Я очень, очень смущен. Пожалуйста, пролите немного света?

Ответы [ 8 ]

321 голосов
/ 09 октября 2008

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

public boolean equals(Object other);

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

ArrayList использует правильный метод equals, где вы всегда вызывали тот, который не корректно перекрывал Object '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 может помочь тонне с глупыми ошибками.

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

107 голосов
/ 02 июня 2011

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

Источник -> Создать равно () и хэш-код ()

11 голосов
/ 09 октября 2008

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

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

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

4 голосов
/ 17 октября 2014

Еще одним быстрым решением, которое сохраняет стандартный код, является Аннотация 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 . Просто добавьте его с при условии scope:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.14.8</version>
    <scope>provided</scope>
</dependency>
1 голос
/ 09 сентября 2015

Рассмотрим:

Object obj = new Book();
obj.equals("hi");
// Oh noes! What happens now? Can't call it with a String that isn't a Book...
1 голос
/ 22 августа 2015

в Android Studio есть alt + insert ---> equals and 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();
}
0 голосов
/ 24 августа 2015

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;
    }
0 голосов
/ 30 января 2015

оператор instanceOf часто используется в реализации equals.

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

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

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

если первое значение равно true, а object2 является экземпляром подкласса класс, которому принадлежит obj1, тогда второе равенство вернет false!

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...