Чому .compareTo () в інтерфейсі, а .equals () знаходиться в класі на Java?


30

Хочу знати, чому .compareTo()в Comparableінтерфейсі, а такий метод, як .equalsу Objectкласі. Мені здається довільним, чому такий метод, як, наприклад, .compareTo()вже не в Objectкласі.

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

Моє питання: чому такий метод, як .compareTo()в інтерфейсі, який ви реалізуєте, а не в класі, як Object? Так само, чому .equals()метод реалізується в класі, Objectа не в якомусь інтерфейсі?



2
Це вибір дизайну мови Java (не обов'язково означає, що це був правильний вибір). Іншими мовами, наприклад Haskell , вам потрібно реалізувати інтерфейс рівності, щоб отримати рівність значень (фактично ви надаєте екземпляр типу Eqtype).
mucaho

2
Пов'язане мета-запитання: чи ці запитання дублікати між собою?

Відповіді:


58

Не всі об'єкти можна порівняти, але всі об’єкти можна перевірити на рівність. Якщо нічого іншого, то можна побачити, чи є два об’єкти в одному місці в пам'яті (еталонна рівність).

Що це означає для compareTo()двох Threadоб'єктів? Як одна нитка "більша за" іншу? Як ти порівнюєш дваArrayList<T> с?

ObjectДоговір стосується всіх класів Java. Якщо навіть один клас не можна порівняти з іншими примірниками власного класу, тоObject не може вимагати, щоб він був частиною інтерфейсу.

Джошуа Блох використовує ключові слова "природне впорядкування", коли пояснює, чому клас може хотіти реалізувати Comparable. Не кожен клас має природне впорядкування, як я згадував у своїх прикладах вище, тому не кожен клас повинен реалізовувати, Comparableа також не повинен Objectмати compareToметод.

... compareToметод не оголошено в Object. ... Він схожий за характером Objectз equalsметодом, за винятком того, що дозволяє порівняти замовлення на додаток до простих порівнянь рівності, і він є загальним. Реалізуючи Comparable, клас вказує, що його екземпляри мають природне впорядкування .

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

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


3
Ще веселіше виникає, коли ти починаєш думати про порівняння з винятком (який не є абстрактним класом, і, таким чином, він би реалізував це означає, що aNullPointerException.compareTo (anUnsupportedFlavorException) мав би ... сенс?

10
всі об'єкти можна перевірити на рівність У Java так, але взагалі ні. Є декілька прикладів об'єктів, де порівняння рівності не має сенсу (навіть рівне відношення) - наприклад, одиночні. Можуть бути такі інтерфейси (абстрактні класи), як ValueEquality та ReferenceEquality. Це може бути навіть не така
гарна

5
"всі об'єкти можна перевірити на рівність. Якщо нічого іншого, можна побачити, чи є два об'єкти в одному місці в пам'яті (еталонна рівність)." - оскільки ми маємо ==для останнього, це має порожнє кільце до нього. Не зважаючи на надлишкові значення за замовчуванням, можна знайти поважні причини не брати участь equalsу всіх класах, оскільки не всі типи можуть підтримувати відношення еквівалентності.
Рафаель

3
Два приклади типів, де не доцільно визначити рівність: потоки (наприклад, ліниві потенційно нескінченні списки або нескінченні точності чисел) та функції. Перший має проблему, що встановлення рівності може вимагати порівняння нескінченно. Вирішити, чи дві функції рівні, не можна визначити. Запитуючи, чи існують два екземпляри цих типів в одному і тому ж місці пам'яті, це 1) не дуже корисно і 2) дозволяє клієнтам писати код, який чутливий до того, що має бути деталізацією щодо реалізації. Сьогодні я можу надати вам новий екземпляр того самого нескінченного списку кожного разу, коли ви запитаєте, завтра я можу запам'ятати його.
Довал

6
@Snowman Той факт, що він марний у поєднанні з тим, що викриває деталі реалізації, є достатньою причиною, щоб не допустити цього. Практично кожен клас "на основі цінності" у Java 8 має певну табличку, яка говорить: "Ми не несемо відповідальності за те, що станеться, якщо ви використовуєте ==", оскільки те, як ці класи створені, є деталізацією реалізації, але мова не дозволяє приховати. Можна сказати, що той, хто порівнює два Integers за посиланням, - ідіот, але дозволяти порівняння починати ще тупіше.
Довал

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дуже корисний разом із методом, рівним, у хешшлях, таких як java.util.Hashmap.

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

  • Метод toStringповертає String представлення об'єкта.

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


Я думаю, теоретично може бути інтерфейс, Equitable<T>але якби Objectйого реалізували, то кожен клас був би Equitable<Object>. Чи є в цьому момент різниця? Насправді не дуже, за складністю так.
Людина капітана

1
@CaptainMan In .Net objectмає Equals(object), як і в Java, але є і IEquatable<T>інтерфейс. Хоча головна причина його існування - це уникати боксу, коли Tце тип значення, що неможливо на Java.
svick

hashCode не є формою тестування рівності, оскільки є хеш-колізії. якщо A і B рівні, у них однаковий хеш-код, але якщо A і B мають однаковий хеш-код, це не означає, що вони рівні!
Йозеф

насправді, ваша відповідь дуже допомогла б перейти на старіший 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

Окрім відмінної відповіді Сніговика, пам’ятайте, що Comparableце був загальний інтерфейс вже давно. Тип не реалізує compareTo(object), він реалізує compareTo(T)там, де Tє власний тип. Це неможливо реалізувати object, оскільки objectвін не знає класу, який буде походити з нього.

objectможна було б визначити compareTo(object)метод, але це дозволило б не лише те, що вказує Сніговик, порівняння між двома ArrayList<T>або між двома Thread, але навіть порівняння між a ArrayList<T>і a Thread. Це ще більш безглуздо.


0

Припустимо, у мене є дві посилання на об'єкт: X ідентифікує примірник Stringвмісту вмісту "Джордж"; Y ідентифікує примірник Pointкоординат [12,34]. Розглянемо наступні два питання:

  • Чи ідентифікують X і Y еквівалентні об'єкти?

  • Чи повинен X сортувати до, після або еквівалентний Y?

Той факт, що X і Y ідентифікують випадки споріднених типів, не створює жодних проблем при розгляді першого питання. Об'єкти можна вважати еквівалентними лише у тому випадку, якщо їх типи мають спільну базу, яка визначає їх як еквівалентні; оскільки Stringі Pointне мають такої бази (єдиний їх загальний тип бази розглядає всі окремі об'єкти як нееквівалентні), відповідь просто "ні".

Той факт, що типи не пов'язані між собою, створює величезну проблему щодо другого питання. Деякі типи визначають відносини впорядкування між їхніми примірниками, а деякі відносини впорядкування можуть навіть поширюватися на кілька типів [наприклад, це можна було б визначити BigIntegerі BigDecimalвизначити методи порівняння, які дозволяли б класифікувати екземпляри будь-якого типу відносно випадків іншого], але загалом неможливо взяти два довільні екземпляри і запитати "Чи слід X сортувати до, після або еквівалентно Y" та отримати загальне впорядкування. Можна було б запитати "Чи слід X сортувати до, після, еквівалентного чи невизначеного відносно Y", якщо об'єкти повинні були повідомити про послідовне впорядкування, але не загальнеОдин, але більшість алгоритмів сортування вимагає загального замовлення. Таким чином, навіть якщо всі об'єкти могли реалізувати compareToметод, якщо "unranked" був дійсним поверненням, такий метод був би недостатньо корисним для обгрунтування його існування.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.