Почему метод Java Area # equals не переопределяет Object # equals?
Я столкнулся с проблемой, вызванной Java java.awt.geom.Area#equals(Area)
. Проблема может быть упрощена до следующих unit test:
@org.junit.Test
public void testEquals() {
java.awt.geom.Area a = new java.awt.geom.Area();
java.awt.geom.Area b = new java.awt.geom.Area();
assertTrue(a.equals(b)); // -> true
java.lang.Object o = b;
assertTrue(a.equals(o)); // -> false
}
После некоторой царапины и отладки головы я наконец увидел в источнике JDK, что подпись метода equals
в Area
выглядит следующим образом:
public boolean equals(Area other)
Обратите внимание, что это не @Override
обычный equals
метод из Object
, а вместо этого просто перегружает метод более конкретным типом. Таким образом, два вызова в приведенном выше примере заканчиваются вызовом различных реализаций equals
.
Поскольку это поведение присутствует с Java 1.2, я предполагаю, что это не считается ошибкой. Поэтому я больше заинтересован в том, чтобы выяснить, почему было принято решение не правильно переопределять метод equals
, но в то же время обеспечить перегруженный вариант. (Еще один намек на то, что это было фактически принято, - отсутствие перезаписанного метода hashCode()
.)
Мое единственное предположение заключалось в том, что авторы опасались, что медленная реализация equals
для областей непригодна для сравнения равенства при размещении Area
в Set
, Map
и т.д. datastructures. (В приведенном выше примере вы могли бы добавить a
в HashSet
, и хотя b
равен a
, вызов contains(b)
завершится с ошибкой.) И опять же, почему они не просто назвали сомнительный метод таким образом, чтобы он не сталкивался с таким фундаментальным понятием, как метод equals
?
Ответы
Ответ 1
RealSkeptic, связанный с JDK-4391558 в комментарии выше. comment в этой ошибке объясняется аргументация:
Проблема с переопределением равно (Object) заключается в том, что вы также должны переопределить hashCode(), чтобы вернуть значение, которое гарантирует, что equals() true, только если хэш-коды двух объектов также равны.
а
Проблема здесь в том, что Area.equals(Area) не выполняет очень прямолинейное сравнение. Он тщательно анализирует каждый кусок геометрии в двух областях и тесты, чтобы увидеть, покрывают ли они те же замкнутые пространства. Объекты Two Area могут иметь полностью другое описание того же замкнутого пространства и равно (Площадь) будет обнаруживать, что они были одинаковыми.
Таким образом, в основном у нас остается массив не очень приятных опций, например:
deprecate equals (Area) и создать альтернативное имя для этого например, "areasEqual", чтобы избежать путаницы. К сожалению, старый метод останется и будет связан и поймал бы многих людей, которые намеревались вызвать равных (Объект) версия.
или
deprecate equals (Area) и изменить его реализацию точно равным (Object), чтобы избежать семантических проблем, если неправильные метод. Создайте новый метод с другим именем, чтобы избежать путаница для реализации старой функциональности, предоставляемой равными (Area).
или
реализовать equals (Object) для вызова equals (Area) и реализовать манекен hashCode(), который чтит контракт equals/hashCode в вырожденном путем возврата константы. Это сделает метод hashCode практически бесполезны и делают объекты Area почти бесполезными, поскольку ключи в HashMap или Hashtable.
или другие способы изменения поведения equals(Area)
, которые либо изменят его семантику, либо сделают ее несовместимой с hashCode
.
Похоже, что этот метод считается, что сопровождающие не могут быть ни осуществимы (поскольку ни один из вариантов, описанных в комментарии к ошибке, не решает проблему), так и не важно (поскольку метод, реализованный, довольно медленный и, вероятно, только когда-либо вернется true при сравнении экземпляра Area
с самим собой, как предлагает комментатор).
Ответ 2
"Почему метод Java Area # equals не переопределяет Object # равно?"
Поскольку переопределение не требуется для перегруженных методов, где параметры имеют разные типы.
Переопределенный метод будет иметь то же самое имя метода, тип возврата, количество параметров и типы параметров как метод в родительском классе, и единственным отличием будет определение метода.
Этот случай не заставляет нас переопределять, но он перегружен, поскольку он следует этим правилам:
1.) Число параметров для методов отличается.
2.) Типы параметров различны (например, изменение параметра, который был float для int).
"почему они не просто назвали сомнительный метод таким образом, чтобы он не сталкивался с таким фундаментальным понятием, как метод equals?"
Потому что это может привести людей в будущее. Если бы у нас была машина времени на 90, мы могли бы сделать это без этой проблемы.