Рассмотрим следующий фрагмент:
import java.util.*;
public class EqualsOverload {
public static void main(String[] args) {
class Thing {
final int x;
Thing(int x) { this.x = x; }
public int hashCode() { return x; }
public boolean equals(Thing other) { return this.x == other.x; }
}
List<Thing> myThings = Arrays.asList(new Thing(42));
System.out.println(myThings.contains(new Thing(42))); // prints "false"
}
}
Обратите внимание, что contains
возвращает false
!!! Мы, кажется, потеряли наши вещи!
Ошибка, конечно же, в том, что мы случайно перегрузили, а не переопределили Object.equals(Object)
. Если бы мы написали class Thing
следующим образом, то contains
возвращает true
, как ожидалось.
class Thing {
final int x;
Thing(int x) { this.x = x; }
public int hashCode() { return x; }
@Override public boolean equals(Object o) {
return (o instanceof Thing) && (this.x == ((Thing) o).x);
}
}
Эффективное Java 2nd Edition, пункт 36: последовательно использовать аннотацию Override, использует по существу тот же аргумент, чтобы рекомендовать использовать @Override
последовательно. Разумеется, этот совет хорош, потому что если бы мы попытались объявить @Override equals(Thing other)
в первом фрагменте, наш дружелюбный маленький компилятор немедленно укажет на нашу глупую небольшую ошибку, поскольку это перегрузка, а не переопределение.
Однако в книге особо не говорится о том, стоит ли начинать перегрузку equals
. По существу, есть три ситуации:
- Только перегрузка, без переопределения - ПОЧТИ НЕКОТОРЫЕ НЕПРАВИЛЬНЫЕ!
- Это, по сути, первый фрагмент выше
- Только переопределение (без перегрузки) - один из способов исправить
- Это по существу второй фрагмент выше
- Перегрузка и переопределение комбо - другой способ исправления.
Третья ситуация иллюстрируется следующим фрагментом:
class Thing {
final int x;
Thing(int x) { this.x = x; }
public int hashCode() { return x; }
public boolean equals(Thing other) { return this.x == other.x; }
@Override public boolean equals(Object o) {
return (o instanceof Thing) && (this.equals((Thing) o));
}
}
Здесь, хотя у нас теперь есть метод 2 equals
, существует еще одна логика равенства и находится в перегрузке. @Override
просто делегирует перегрузку.
Итак, вопросы:
- Каковы плюсы и минусы "переопределения только" против "перегрузки и переопределения комбо"?
- Есть ли оправдание перегрузки
equals
, или это почти наверняка плохой способ?