Рассмотрим следующий фрагмент:
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);
}
}
Effective Java 2nd Edition, пункт 36: последовательно используйте аннотацию Override , использует, по сути, тот же аргумент, чтобы рекомендовать, чтобы @Override
использовалось последовательно.Конечно, этот совет хорош, поскольку, если бы мы попытались объявить @Override equals(Thing other)
в первом фрагменте, наш дружелюбный маленький компилятор немедленно указал бы на нашу маленькую глупую ошибку, поскольку это перегрузка, а не переопределение.
* 1026Однако то, что в книге конкретно не освещено, является ли перегрузкой
equals
хорошей идеей для начала.По сути, существует 3 ситуации:
- Только перегрузка, без переопределения - ПОЧТИ НЕОБХОДИМО НЕПРАВИЛЬНО !
- Это по сути первый фрагмент кода выше
- Только переопределение (без перегрузки) - один из способов исправить
- Это по сути второй фрагмент кода выше
- Комбинация перегрузки и переопределения - еще один способ исправить
Третья ситуация иллюстрируется следующим фрагментом:
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
или это почти наверняка плохая практика?