Почему .compareTo () в интерфейсе, а .equals () в классе в Java?

30

Я хочу знать, почему интерфейс .compareTo()находится в Comparableинтерфейсе, а метод like .equals- в Objectклассе. Мне кажется произвольным, почему такой метод .compareTo()уже не в Objectклассе.

Чтобы использовать .compareTo(), вы реализуете Comparableинтерфейс и реализуете .compareTo()метод для ваших целей. Для .equals()метода вы просто переопределяете метод в своем классе, так как все классы наследуются от Objectкласса.

Мой вопрос: почему метод похож .compareTo()на интерфейс, который вы реализуете, а не на класс Object? Аналогично, почему .equals()метод в классе, Objectа не в каком-то интерфейсе, должен быть реализован?

Wesley
источник
2
Это выбор дизайна языка Java (не обязательно означает, что это был правильный выбор). В других языках, например, на Haskell , вы должны реализовать интерфейс равенства, чтобы получить равенство значений (фактически вы предоставляете экземпляр для Eqкласса типов).
Mucaho

Ответы:

58

Не все объекты можно сравнивать, но все объекты можно проверить на равенство. Если ничего другого, то можно увидеть, существуют ли два объекта в одном и том же месте в памяти (равенство ссылок).

Что это значит для compareTo()двух Threadобъектов? Как один поток "больше" другого? Как вы сравниваете два ArrayList<T>с?

ObjectДоговор относится ко всем классам Java. Если даже один класс нельзя сравнить с другими экземплярами его собственного класса, он Objectне может требовать, чтобы он был частью интерфейса.

Джошуа Блох использует ключевые слова «естественный порядок», когда объясняет, почему класс может захотеть реализовать Comparable. Не каждый класс имеет естественный порядок, как я упоминал в моих примерах выше, поэтому не каждый класс должен реализовывать и Comparableне должен Objectиметь compareToметод.

... compareToметод не объявлен в Object. ... Он похож по своему характеру Object«S equalsметоде, за исключением того, что она позволяет сравнивать порядок , в дополнении к простым сравнению равенства, и это является общим. Реализуя Comparable, класс указывает, что его экземпляры имеют естественный порядок .

Эффективная Java, второе издание : Джошуа Блох. Пункт 12, стр. 62. Эллипсы удаляют ссылки на другие главы и примеры кода.

Для случаев , когда вы действительно хотите , чтобы наложить упорядочение на не- Comparableкласса , который не имеет естественный порядок, вы всегда можете поставить Comparatorэкземпляр для помощи разбирайтесь.


источник
3
Еще более увлекательно, когда вы начинаете думать о CompareTo с Exception (которое не является абстрактным классом и, следовательно, реализовало бы его, означая, что aNullPointerException.compareTo (anUnsupportedFlavorException) будет иметь ... значение?
10
все объекты можно проверить на равенство. В Java да, но в целом нет. Есть несколько примеров объектов, где сравнение на равенство не имеет смысла (даже ссылочное равенство) - например, синглтоны. Могут быть интерфейсы (абстрактные классы), такие как ValueEquality и ReferenceEquality. Это может быть даже не такая плохая идея ...
QBD
5
«все объекты можно проверить на равенство. Если ничего другого, то можно увидеть, существуют ли два объекта в одном месте в памяти (ссылочное равенство)». - поскольку у нас есть ==для последнего, это имеет полое кольцо к нему. Независимо от избыточного значения по умолчанию, можно найти веские причины не принимать equalsво внимание все классы, поскольку не все типы могут поддерживать отношение эквивалентности.
Рафаэль
3
Два примера типов, в которых нет смысла определять равенство: потоки (например, ленивые потенциально бесконечные списки или числа с бесконечной точностью) и функции. У первого есть проблема, заключающаяся в том, что установление равенства может потребовать бесконечного сравнения. Решение, равны ли две функции, неразрешимо. Вопрос о том, существуют ли два экземпляра этих типов в одной и той же области памяти: 1) не очень полезен и 2) позволяет клиентам писать код, чувствительный к деталям реализации. Сегодня я могу каждый раз давать вам новый экземпляр того же бесконечного списка, завтра я могу его запомнить.
Довал
6
@ Snowman Тот факт, что это бесполезно в сочетании с фактом, раскрывающим детали реализации, является достаточной причиной, чтобы этого не допустить. Практически каждый «основанный на значениях» класс в Java 8 имеет некоторый шаблон, говорящий «Мы не несем ответственности за то, что произойдет, если вы используете ==», потому что то, как создаются эти классы, является деталью реализации, но язык делает невозможным скрытие. Можно сказать, что любой, кто сравнивает два Integerс помощью ссылки, - идиот, но позволить сравнение с самого начала еще глупее.
Довал
8

В ПСБ §4.3.2 определяет, какой classобъект следующим образом:

4.3.2. Объект класса

Класс Objectявляется суперклассом (§8.1.4) всех других классов.

Все классы и типы массивов наследуют (§8.4.8) методы класса Object, которые суммируются следующим образом:

  • Метод cloneиспользуется для создания дубликата объекта.

  • Метод equalsопределяет понятие равенства объектов, основанное на сравнении, а не на значении.

  • Метод finalizeзапускается непосредственно перед уничтожением объекта (§12.6).

  • Метод getClassвозвращает объект Class, который представляет класс объекта.

  • ClassОбъект существует для каждого ссылочного типа. Его можно использовать, например, для обнаружения полностью определенного имени класса, его членов, его непосредственного суперкласса и любых интерфейсов, которые он реализует.

    Тип выражения вызова метода getClass- это то место, Class<? extends |T|>где Tищется класс или интерфейс (§15.12.1) getClass.

    Объявленный метод класса synchronized(§8.4.3.6) синхронизируется на мониторе, связанном с объектом Class класса.

  • Метод hashCodeочень полезен вместе с методом equals в таких хеш-таблицах, как java.util.Hashmap.

  • Методы wait, notifyи notifyAllиспользуются в параллельном программировании с использованием нитей (§17.2).

  • Метод toStringвозвращает строковое представление объекта.

Итак, вот почему equalsв Objectно compareToв отдельном интерфейсе. Я бы предположил, что они хотят сохранить Objectкак можно меньше. Они, вероятно, полагали, что почти всем Objects понадобится equalsи hashCode(что на самом деле является просто формой проверки на равенство), но не все объекты должны иметь концепцию упорядочения , для чего compareToи используется.

durron597
источник
Я предполагаю, что теоретически может быть интерфейс, Equitable<T>но если Objectего реализовать, то каждый класс будет Equitable<Object>. На этом этапе есть разница? В действительности не очень, по сложности да.
Капитан Мэн
1
@CaptainMan В .Net, objectимеет Equals(object), как и в Java, но есть также IEquatable<T>интерфейс. Хотя основная причина его существования заключается в том, чтобы избегать бокса, когда Tэто тип значения, что невозможно в Java.
свик
hashCode не является формой проверки на равенство, потому что есть коллизии хешей. если A и B равны, они имеют одинаковый hashCode, но если A и B имеют одинаковый hashCode, это не означает, что они равны!
Иосиф
на самом деле, ваш ответ очень выиграл бы от перехода на более старую версию JLS ( titanium.cs.berkeley.edu/doc/java-langspec-1.0.pdf ) - в нем гораздо лучше процитировать, почему equalsон объявлен Objectнапрямую: The methods equals and hashCode are declared for the benefit of hashtables such as java.util.Hashtable (§21.7)- как дизайн Java является обратно совместимым, выбор дизайна Java 1.0 является реальной причиной equalsтого, где он находится.
vaxquis
поскольку предложенное мной редактирование, вероятно, будет отклонено как «слишком радикальное», на случай, если вы захотите просто нажать Ctrl + C / Ctrl + V на соответствующий ответ в своем ответе: pastebin.com/8c4EpLRX
vaxquis
2

В дополнение к отличному ответу Snowman, помните, что Comparableэто был общий интерфейс в течение долгого времени. Тип не реализует compareTo(object), он реализует, compareTo(T)где Tэто его собственный тип. Это не может быть реализовано object, так objectкак не знает класс, который будет производным от него.

objectмог бы определить compareTo(object)метод, но это позволило бы не только то, на что указывает Снеговик, сравнение между двумя ArrayList<T>s или между двумя Threads, но даже сравнение между a ArrayList<T>и a Thread. Это еще более бессмысленно.

HVD
источник
0

Предположим, у меня есть две ссылки на объект: X идентифицирует экземпляр Stringсодержания «Джордж»; Y идентифицирует экземпляр Pointудержания координат [12,34]. Рассмотрим следующие два вопроса:

  • X и Y идентифицируют эквивалентные объекты?

  • Должен ли X сортироваться до, после или эквивалентно Y?

Тот факт, что X и Y идентифицируют экземпляры несвязанных типов, не представляет никаких проблем при рассмотрении первого вопроса. Объекты могут считаться эквивалентными, только если их типы имеют общую базу, которая определяет их как эквивалентные; так как Stringи не Pointимеют такой базы (их единственный общий базовый тип рассматривает все различные объекты как неэквивалентные), ответ просто «нет».

Однако тот факт, что типы не связаны между собой, создает огромную проблему в отношении второго вопроса. Некоторые типы определяют отношения упорядочения среди своих экземпляров, а некоторые отношения упорядочения могут даже распространяться на несколько типов [например, было бы возможно BigIntegerи BigDecimalопределить методы сравнения, которые позволили бы ранжировать экземпляры одного типа относительно экземпляров другого], но в общем случае невозможно взять два произвольных экземпляра и спросить «Должен ли X сортировать до, после или эквивалентен Y» и получить полное упорядочение. Можно было бы спросить «Должен ли X сортировать до, после, эквивалентный или не оцененный по отношению к Y», требуется ли объектам сообщать о последовательном упорядочении, хотя и не об общемодин, но большинство алгоритмов сортировки требуют полного упорядочения. Таким образом, даже если бы все объекты могли реализовать compareToметод, если «unranked» был допустимым возвращением, такой метод не был бы достаточно полезен, чтобы оправдать его существование.

Supercat
источник