Различать подклассы с помощью equals & hashcode

1

Я ищу руководство относительно переопределения как hashcode, так и equals в подклассе.

Я нашел аналогичный вопрос здесь: Переопределение equals() и hashCode() в подклассах... с учетом суперполей

Но то, что я хочу, немного отличается.

Представьте себе этот (несколько глупый) пример:

class Food {

  String name;

  @Override
  public boolean equals(Object obj) {
    if (obj instanceof Food) {
      Food other = (Food)obj;
      return name.equals(other.name);
    }
    return false;
  }

  @Override
  public int hashCode() {
    return name.hashCode();
  }
}

class Vegetable extends Food {
  // No additional fields here
  // Some methods here
}

class Fruit extends Food {
  // No additional fields here
  // Some methods here
}

При условии:

  1. Подклассы не добавляют лишние поля
    • в этом примере, по крайней мере, это в основном классы маркеров
  2. Fruit и Vegetable с одинаковым названием не должны быть равны

Вопросов:

  1. Ожидаете ли вы, что подкласс равен super.equals instanceof проверки для подкласса и вызову super.equals?
  2. Как следует структурировать хэш-код в попытке иметь экземпляры Fruit and Vegetable с тем же именем, имеющие разные хэш-коды?
Теги:
equals
hashcode

2 ответа

2
Лучший ответ

Вы можете просто использовать .getClass() в Foo .equals():

@Override
public final boolean equals(final Object obj)
{
    if (obj == null)
        return false;
    if (this == obj)
        return true;
    // Class<?> are signletons, therefore we can use ==/!=
    if (getClass() != obj.getClass())
        return false;
    return name.equals(((Foo) obj).name);
}

.getClass() вернет класс объекта, поэтому Fruit or Vegetable, или даже Food.

Для .hashCode() name.hashCode() отлично. Не забывайте, что нет требования, чтобы хэш-код двух объектов, которые не являются .equals(), различны. Но если вы хотите, то здесь также можно использовать .getClass():

@Override
public final int hashCode()
{
    return 31 * getClass().hashCode() + name.hashCode();
}
  • 0
    Да, кажется справедливым, за исключением того, что я пытался быть хорошим и пытался придерживаться идеи Джоша Блоха «Всегда переопределять hashCode, когда вы переопределяете равно»
  • 1
    Ну, вы переопределяете hashCode. Опять же, не существует требования, что если !o1.equals(o2) то o1.hashCode() != o2.hashCode() . Или, может быть, вы неверно истолковали часть «вам все равно». Я имел в виду "это нормально": =)
Показать ещё 1 комментарий
1
  1. Вы ожидали бы, что равные просто будут содержать проверку экземпляра и вызов super.equals?

instanceof здесь опасен, потому что Food не абстрактна. Это означает, что equals не были симметричными.

someFruit.equals(someFood) // will be false
someFood.equals(someFruit) // will be true

Это может не превратиться в проблему, но это то, что вы должны учитывать.

Если вы не хотите разорвать контракт, это случай, когда Food должна проверить, является ли this.getClass() == obj.getClass(). Если вы это сделаете, вам также необязательно будет переопределять его в подклассах.

В противном случае это не имеет большого значения. Метод определен по контракту, и вы можете реализовать его, как хотите.

  1. Как следует структурировать хэш-код в попытке иметь экземпляры Fruit and Vegetable с тем же именем, имеющие разные хэш-коды?

Им не нужно быть разными.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню