2014-03-17 4 views
1

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

Я нашел аналогичный вопрос здесь: Overriding equals() & hashCode() in sub classes ... considering super fields

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

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

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. Вы бы ожидать, что подкласс равно просто содержать instanceof проверку подкласса и вызова super.equals?
  2. Как следует структурировать хэш-код в попытке иметь Fruit и Vegetable экземпляры с тем же именем имеют разные хэш-коды?
+0

Ха-ха, привет, привет! –

+1

вы можете использовать имя класса в качестве компонента хэш-кода (например: '17 * classname.hashcode() + super.hashcode()') –

+1

Обратите внимание, что многие структуры данных в Java API показывают поведение, которое вы показаны здесь. Взгляните на все виды Карт. Например, HashMap может равняться LinkedHashMap. Карты действительно проверяют содержимое Карты. Класс реализации не имеет большого значения. –

ответ

2

Вы можете просто использовать .getClass() в Foo «s .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 или 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)' then 'o1.hashCode()! = O2.hashCode()'. Или, может быть, вы неверно истолковали часть «вас не волнует». Я имел в виду «это нормально»: =) – fge

+0

Обратите внимание, что это законная реализация hashCode(), хотя довольно бесполезная: 'return 42;' – fge

1
  1. Вы ожидали бы, что равно просто содержать проверку InstanceOf и вызов super.equals?

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

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

Это не может поворот к проблеме но это вещь, которую вы должны рассмотреть.

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

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

  1. Как следует хэш быть структурирован в попытке иметь фруктовые и овощные экземпляры с таким же именем имеет разное hashcodes?

Они не должны быть разными.

Смежные вопросы