Контекст из книги Брюс Эккель "Философия Java" (полное издание) стр.674 - 677.
Сначала автор создаёт наивную реализацию сравнения и показывает что она не работает.
Например, рассмотрим систему прогноза погоды, которая ставит в соответствие объекты Groundhog (сурок) объектам Prediction (прогноз погоды). Все выглядит довольно просто — вы создаете два класса: Groundhog для ключей таблицы и Prediction для сопоставленного
ему значения:
//: containers/Groundhog.java
public class Groundhog {
protected int number;
public Groundhog(int n) { number = n; }
public String toString() {
return "Groundhog #" + number;
}
} ///:~
public class Prediction {
private static Random rand = new Random(47);
private boolean shadow = rand.nextOouble() > 0.5;
public String toString() {
if(shadow)
return "Six more weeks of Winter!";
else
return "Early Spring!";
}
} ///:~
public class SpringDetector {
// Используем Groundhog или класс, производный от него:
public static <T extends Groundhog>
void detectSpring(Class<T> type) throws Exception {
Constructor<T> ghog = type.getConstructor(int.class);
Map<Groundhog,Prediction> map = new HashMap<Groundhog,Prediction>();
for(int i = 0; i < 10j i++)
map.put(ghog.newInstance(i), new Prediction());
print("map = " + map);
Groundhog gh = ghog.newInstance(3);
print("Looking up prediction for " + gh);
if(map.containsKey(gh))
print(map.get(gh));
else
print("Key not found: " + gh);
}
public static void main(String[] args) throws Exception {
detectSpring(Groundhog.class);
}
) /* Output:
map = {Groundhog #3=Early Springl, Groundhog #7=Early
SpringI, Groundhog #5=Early Spring!, Groundhog #9=Six more
weeks of Winter!, Groundhog #8=Six more weeks of Winter!,
Groundhog #0=Six more weeks of Winter!, Groundhog #6=Early
Springl, Groundhog #4=Six more weeks of Winter!, Groundhog
#l=Six more weeks of Winter!, Groundhog #2=Early Spring!}
Looking up prediction for Groundhog #3
Key not found: Groundhog #3
Далее автор говорит что это не работает и надо изменить класс, переопределив hashCode и equals чтобы можно было правильно сравнить.
Все выглядит просто, но тем не менее не работает. Проблема состоит в том, что класс
Groundhog унаследован от общего корневого iomccaObject, а для генерирования хеш-кода
объекта используется реализация hashCode() этого класса. По умолчанию этот метод
возвращает адрес объекта. Таким образом, хеш-код первого экземпляра Groundhog(3)
не совпадает с хеш-кодом второго экземпляра Groundhog(3), который применялся
в качестве ключа при поиске.
Можно предположить, что необходимо просто переопределить метод hashCode(), на
писав в классе свою собственную версию. Но и это не поможет до тех пор, пока вы не
сделаете еще одну вещь: не переопределите метод equals(), который также является
частью класса Object. Этот метод используется картой HashMap для проверки того, равен
ли ваш ключ какому-либо ключу из содержащихся в ней.
Метод equals() в классе Object по умолчанию сводится к сравнению адресов, поэтому
один объект Groundhog(3) никогда не будет равен другому объекту Groundhog(3). Таким
образом, чтобы задействовать ваши собственные классы в качестве ключей HashMap,
необходимо переопределить и метод hashCode(), и метод equals(), как показано в сле
дующем примере, который исправляет ошибку предыдущей программы:
//: containers/Groundhog2.java
// Класс, используемый в качестве ключа HashMap,
// должен переопределять hashCode() и equals().
public class Groundhog2 extends Groundhog {
public Groundhog2(int n) { super(n); }
public int hashCode() { return number; }
public boolean equals(Object о) {
return о instanceof Groundhog2 &&
(number == ((Groundhog2)o).number);
}
} ///:~
//: containers/SpringDetector2.java
// Работоспособный класс ключа
public class SpringDetector2 {
public static void main(String[] args) throws Exception {
SpringDetector.detectSpring(Groundhog2.class);
}
> /* Output:
map = {Groundhog #2=Early Spring!, Groundhog #4=Six more
weeks of Winter!, Groundhog #9=Six more weeks of Winter!,
Groundhog #8=Six more weeks of Winter!, Groundhog #6=Early
Spring!, Groundhog tl=Six more weeks of Winter!, Groundhog
#3=Early Spring!, Groundhog #7=Early Spring!, Groundhog
#5=Early Spring!, Groundhog #0=Six more weeks of Winter!}
Looking up prediction for Groundhog #3
Early Spring!
*///:~
далее непонятно что с чем сравнивается в этой строке
(number == ((Groundhog2)o).number);
number это же объявленный protected int number; в классе public class Groundhog, тогда что значит
((Groundhog2)o).number
?
И как это кстати читается? Поле объекта-аргумента
Object о
приведённое к классу
Groundhog2
?
Далее автор пишет, что
Здесь метод equals() основывается на номере сурка; таким образом, если два объекта Groundhog2 существуют как ключи в HashMap под одним и тем же номером, объекты будут считаться неравными.
Я уже совсем запутался, если номера(ключи) у них одинаковые то и они должны быть равны, разве нет?