Должен ли мой метод equals / hashCode проверять больше, чем идентификаторы объектов? - PullRequest
2 голосов
/ 20 августа 2011

В моем приложении у меня есть классы моделей следующей формы:

class Book
{
    private int ID;
    private String title;

    //other code
}

Теперь мой вопрос состоит из двух частей:

  1. Является ли следующая хорошая реализация метода equals ()?

    public boolean equals(Object o)
    {
        if(o == null)
        {
            return false;
        }
    
        if(!(o instanceof Book))
        {
            return false;
        }
    
        Book other = (Book)o;
    
        if(o.getID() == ID)
        {
            return true;
        }
        return false;
    }
    

    Я знаю, что реализация equals () во многом зависит от моего бизнес-логика приложения. Но если две книги имеют одинаковый идентификатор, то в идеале они должны быть одной и той же книгой. Поэтому я смущен Должен ли я проверить на равенство для других полей значения [название, цена и т. д.].

  2. Является ли это хорошей реализацией метода hashCode ():

    public int hashCode()
    {
        return ID;
    }
    

    Я думаю, что разные книги будут иметь разные идентификаторы, и если две книги имеют одинаковый идентификатор, они равны. Отсюда и выше реализация обеспечит хорошее распределение хеш-кода в контекст моей заявки.

Ответы [ 6 ]

4 голосов
/ 20 августа 2011

Просто хотел добавить комментарий к предыдущим ответам.В контракте equals упоминается, что он должен быть симметричным.Это означает, что a.equals(b) если b.equals(a).

Это причина, по которой instanceof обычно не используется в equals (если класс не является окончательным).Действительно, если некоторый подкласс Book (например, ComicsBook) переопределяет equals, чтобы проверить, что другой объект также является экземпляром ComicsBook, вы окажетесь в ситуации, когда экземпляр Book равенэкземпляру ComicsBook, но экземпляр ComicsBook не равен экземпляру Book.

Таким образом, вы должны (за исключением случаев, когда класс является окончательным или в некоторых других редких случаях) скорее сравнивать классы двух объектов:

if (this.getClass() != o.getClass()) {
    return false;
}

Кстати, именно так Eclipse делает, когда генерируетhashCode и equals методы.

3 голосов
/ 21 августа 2011

Если вы используете Hibernate, то вам нужно учитывать некоторые проблемы, связанные с Hibernate.

Hibernate создает прокси для отложенной загрузки.

  • всегда используйте getter для доступа к свойствам другого объекта (вы уже сделали это)
  • , даже если правильно использовать if (!this.getClass().equals(o.getClass())) { return false;} в обычном приложении, произойдет сбой прокси Hibernate (и всех других прокси). Причина в том, что если один из двух является прокси, то классы никогда не будут равны. Итак, тест if(!(o instanceof Book)){return false;} - это то, что вам нужно.

Если вы хотите сделать это симметричным способом, чем взглянуть на org.hibernate.proxy.HibernateProxyHelper.getClassWithoutInitializingProxy() с помощью этого класса, вы можете реализовать:

if (!HibernateProxyHelper.getClassWithoutInitializingProxy(this)
     .equals(HibernateProxyHelper.getClassWithoutInitializingProxy(o))) {
   return false;
}
  • Другая проблема, возможно, связана с идентификатором - когда вы присваиваете id не при создании нового объекта, а позднее при его сохранении, вы можете столкнуться с проблемами. Предположим, такой сценарий: вы создаете новую книгу с идентификатором 0, затем помещаете книгу в HashSet (она будет назначена корзине в хэш-наборе в зависимости от хэш-кода), затем вы сохраняете книгу в базе данных, и идентификатор устанавливается Давайте скажем 1. Но это меняет хеш-код, и у вас будут проблемы с поиском объекта в наборе снова. - Если это проблема или нет для вас, сильно зависит от вашего приложения, архитектуры и того, как вы используете спящий режим.
3 голосов
/ 20 августа 2011

Не делайте этого:

if(o.getID() == ID)

ID - это объект Integer, а не примитив.Сравнение двух разных, но равных Integer объектов с использованием == вернет false.

Используйте это:

if(o.getID().equals(ID))

Вам также необходимо проверить, является ли ID существующим null.

Кроме этого, ваша логика в порядке - вы придерживаетесь контракта, в котором говорится, что два равных объекта должны иметь одинаковый хэш-код, и вы приняли решение бизнес-логики относительно того, что означает равенство- решение, которое может принять только ты (единого правильного ответа).

1 голос
/ 20 августа 2011

Только примечание: Ваш метод равных

public boolean equals(Object o)
{
    if(o == null)
    {
        return false;
    }

    if(!(o instanceof Book))
    {
        return false;
    }

    Book other = (Book)o;

    if(other.getID() == ID)
    {
        return true;
    }
    return false;
}

может быть (эквивалентно) короче написано так:

public boolean equals(Object o) {
    return (o instanceof Book) &&
       ((Book)o).getID == ID;
}

Кроме этого, если ваши идентификаторы разные для разных книг (и одинаковые для одних и тех же книг), это хорошая реализация.

(Но обратите внимание на замечание Дж. Б. Низета: чтобы убедиться, что это остается симметричным, сделайте equals (или весь класс) final.)

1 голос
/ 20 августа 2011

Нет смысла сравнивать два целых числа, например

if(o.getID() == ID) ...

. Это проверяет идентичность.То, что вы хотите, это тест на равенство:

if(ID!=null && ID.equals(o.getID())) ...
0 голосов
/ 20 августа 2011

это зависит!

я думаю, что этот тип реализации в порядке, если вы справитесь с приведенным ниже условием,

у вас две новые книги, у этих двух книг одинаковое название, ониНа самом деле это та же книга, но вы не сохраняете их в базе данных, поэтому у этих двух книг еще нет идентификаторов, при сравнении их равные упадут

...