Dlaczego .compareTo () jest w interfejsie, podczas gdy .equals () jest w klasie w Javie?

30

Chcę wiedzieć, dlaczego .compareTo()jest w Comparableinterfejsie, podczas gdy metoda podobna .equalsjest w Objectklasie. Wydaje mi się arbitralne, dlaczego takiej metody .compareTo()nie ma już w Objectklasie.

Aby użyć .compareTo(), zaimplementuj Comparableinterfejs i zaimplementuj .compareTo()metodę do swoich celów. W przypadku .equals()metody po prostu przesłonisz metodę w swojej klasie, ponieważ wszystkie klasy dziedziczą od Objectklasy.

Moje pytanie brzmi: dlaczego metoda jest taka jak .compareTo()w interfejsie, który implementujesz, a nie w klasie takiej jak Object? Podobnie, dlaczego .equals()metoda Objectma zostać zaimplementowana w klasie, a nie w jakimś interfejsie?

Wesley
źródło
2
Jest to wybór projektowy języka Java (niekoniecznie oznacza, że ​​był to właściwy wybór). W innych językach, np. Haskell , musisz zaimplementować interfejs równości, aby uzyskać równość wartości (w rzeczywistości udostępniasz instancję Eqklasy).
mucaho
2
Powiązane meta pytanie: czy te pytania się powtarzają?

Odpowiedzi:

58

Nie wszystkie obiekty można porównać, ale wszystkie obiekty można sprawdzić pod kątem równości. Jeśli nic więcej, można sprawdzić, czy dwa obiekty istnieją w tym samym miejscu w pamięci (równość odniesienia).

Co to znaczy compareTo()na dwóch Threadobiektach? W jaki sposób jeden wątek jest „większy niż” inny? Jak porównać dwa ArrayList<T>s?

ObjectUmowa ma zastosowanie do wszystkich klas Java. Jeśli nawet jednej klasy nie można porównać do innych instancji własnej klasy, Objectnie może wymagać, aby była częścią interfejsu.

Joshua Bloch używa słów kluczowych „porządek naturalny”, wyjaśniając, dlaczego klasa może chcieć zaimplementować Comparable. Nie każda klasa ma naturalne uporządkowanie, jak wspomniałem w moich przykładach powyżej, więc nie każda klasa powinna implementować Comparableani nie powinna Objectmieć tej compareTometody.

... compareTometoda nie jest zadeklarowana w Object. ... Jest on podobny w charakterze do Object„s equalsmetody, oprócz tego, że pozwala na porównanie rzędu oprócz prostych porównań równości i jest rodzajowy. Poprzez implementację Comparableklasa wskazuje, że jej instancje mają naturalną kolejność .

Effective Java, Second Edition : Joshua Bloch. Punkt 12, strona 62. Elipsy usuwają odniesienia do innych rozdziałów i przykładów kodu.

W przypadkach, gdzie nie chcą narzucić kolejność na niebędącego Comparableklasy, które nie mają naturalną kolejność, zawsze można dostarczyć Comparatorinstancji to rodzaj pomocy.


źródło
3
Jest jeszcze więcej zabawy, gdy zaczniesz myśleć o CompareTo z Exception (który nie jest klasą abstrakcyjną, a zatem zaimplementowałby ją, co oznacza, że ​​aNullPointerException.compareTo (anUnsupportedFlavorException) miałby ... znaczenie?
10
wszystkie obiekty można sprawdzić pod kątem równości W Javie tak, ale ogólnie nie. Istnieje kilka przykładów obiektów, w których porównanie równości nie ma sensu (nawet równości odniesienia) - np. Singletony. Mogą istnieć interfejsy (klasy abstrakcyjne), takie jak ValueEquality i ReferenceEquality. To może nie być taki zły pomysł ...
qbd
5
„wszystkie obiekty można sprawdzić pod kątem równości. Jeśli nic więcej, można sprawdzić, czy dwa obiekty istnieją w tym samym miejscu w pamięci (równość odniesienia)”. - skoro mamy to ==drugie, ma to pusty pierścień. Pomijając zbędne wartości domyślne, można znaleźć uzasadnione powody, dla których nie należy zakładać, że equalswszystkie klasy, ponieważ nie wszystkie typy mogą obsługiwać relację równoważności.
Raphael
3
Dwa przykłady typów, w których nie ma sensu definiowanie równości: strumienie (np. Leniwe potencjalnie nieskończone listy lub nieskończone liczby precyzji) i funkcje. Ten pierwszy ma problem polegający na tym, że ustanowienie równości może wymagać nieskończonego porównywania. Decyzja, czy dwie funkcje są równe, jest nierozstrzygalna. Pytanie, czy dwa wystąpienia tego typu istnieją w tej samej lokalizacji w pamięci, jest 1) niezbyt przydatne i 2) pozwala klientom pisać kod wrażliwy na szczegóły dotyczące implementacji. Dzisiaj mogę dać ci nową instancję tej samej nieskończonej listy za każdym razem, gdy o to poprosisz, jutro mogę ją zapamiętać.
Doval
6
@Snowman Fakt, że jest to bezużyteczne w połączeniu z faktem, że ujawnia szczegóły implementacji, jest wystarczającym powodem, aby na to nie pozwolić. Prawie każda klasa „oparta na wartościach” w Javie 8 ma pewne podsumowanie mówiąc: „Nie ponosimy odpowiedzialności za to, co się stanie, jeśli użyjesz ==”, ponieważ sposób tworzenia tych klas jest szczegółem implementacji, ale język uniemożliwia ukrycie. Można powiedzieć, że ktokolwiek porównujący dwa Integers przez odniesienie jest idiotą, ale pozwolenie na rozpoczęcie porównania jest jeszcze głupsze.
Doval
8

W JLS §4.3.2 definiuje classobiekt w następujący sposób:

4.3.2 Obiekt klasy

Klasa Objectjest nadklasą (§ 8.1.4) wszystkich innych klas.

Wszystkie typy klas i tablic dziedziczą (§8.4.8) metody klas Object, które podsumowano w następujący sposób:

  • Metoda clonesłuży do utworzenia duplikatu obiektu.

  • Metoda equalsdefiniuje pojęcie równości obiektu, które opiera się na porównaniu wartości, a nie odniesienia.

  • Metodę finalizetę uruchamia się tuż przed zniszczeniem obiektu (§12.6).

  • Metoda getClasszwraca obiekt klasy reprezentujący klasę obiektu.

  • Dla Classkażdego typu odwołania istnieje obiekt. Można go na przykład użyć do odkrycia w pełni kwalifikowanej nazwy klasy, jej członków, jej bezpośredniej nadklasy i wszelkich interfejsów, które implementuje.

    Typ wyrażenia wywołania metody getClassto, Class<? extends |T|>gdzie Tjest szukana klasa lub interfejs (§15.12.1) getClass.

    Zadeklarowana metoda klasy synchronized(§ 8.4.3.6) synchronizuje się na monitorze powiązanym z obiektem klasy klasy.

  • Metoda ta hashCodejest bardzo przydatna, podobnie jak metoda równa się, w tablicach skrótów, takich jak java.util.Hashmap.

  • Metody wait, notifyi notifyAllstosowane są w równoległych nitek programowania, za pomocą (§17.2).

  • Metoda toStringzwraca ciąg reprezentujący obiekt.

Właśnie dlatego equalsjest w, Objectale compareTojest w osobnym interfejsie. Spekulowałbym, że chcieli ograniczyć Objectdo minimum. Prawdopodobnie doszli do wniosku, że prawie wszyscy Objects będą potrzebować equalsi hashCode(co jest tak naprawdę tylko formą testowania równości), ale nie wszystkie obiekty będą musiały mieć pojęcie uporządkowania , do czego się compareToto stosuje.

durron597
źródło
Sądzę, że teoretycznie mógłby istnieć interfejs, Equitable<T>ale gdyby został Objectzaimplementowany, każda klasa byłaby Equitable<Object>. Czy w tym momencie jest jakaś różnica? W rzeczywistości niezupełnie, ze złożonością tak.
Captain Man,
1
@CaptainMan w .NET, objectma Equals(object), podobnie jak w Javie, ale istnieje również IEquatable<T>interfejs. Chociaż głównym powodem jego istnienia jest unikanie boksowania, gdy Tjest to typ wartości, co nie jest możliwe w Javie.
svick,
hashCode nie jest formą testowania równości, ponieważ występują kolizje skrótu. jeśli A i B są równe, mają ten sam kod skrótu, ale jeśli A i B mają ten sam kod skrótu, nie oznacza to, że są równe!
Josef
w rzeczywistości twoja odpowiedź bardzo skorzystałaby na przejściu na starszą JLS ( titanium.cs.berkeley.edu/doc/java-langspec-1.0.pdf ) - ma o wiele lepszy cytat, dlaczego equalsjest zadeklarowany Objectbezpośrednio: The methods equals and hashCode are declared for the benefit of hashtables such as java.util.Hashtable (§21.7)- jako projekt Java jest kompatybilny z poprzednimi wersjami, wybór projektu Java 1.0 jest faktycznym powodem, aby equalsbyć tam, gdzie jest.
vaxquis
ponieważ proponowana przeze mnie edycja prawdopodobnie zostanie odrzucona za „zbyt drastyczną”, na wypadek gdybyś chciał po prostu Ctrl + C / Ctrl + V odpowiednie informacje w swojej odpowiedzi: pastebin.com/8c4EpLRX
vaxquis
2

Oprócz doskonałej odpowiedzi Bałwana pamiętaj, że Comparableod dawna jest to ogólny interfejs. Typ nie implementuje compareTo(object), implementuje compareTo(T)gdzie Tjest swój własny typ. Nie można tego zaimplementować object, ponieważ objectnie zna klasy, która będzie z niego wyprowadzona.

objectmógł zdefiniować compareTo(object)metodę, ale pozwoliłoby to nie tylko na to, co wskazuje Snowman, porównanie dwóch ArrayList<T>s lub dwóch Threads, ale nawet porównanie między ArrayList<T>a a Thread. To jeszcze bardziej bezsensowne.

hvd
źródło
0

Załóżmy, że mam dwa odwołania do obiektów: X identyfikuje instancję Stringprzechowywania zawartości „George”; Y identyfikuje przykład Pointtrzymania współrzędnych [12,34]. Rozważ następujące dwa pytania:

  • Czy X i Y identyfikują równoważne obiekty?

  • Czy X powinien sortować przed, po, czy może być równoważny Y?

Fakt, że X i Y identyfikują przypadki niepowiązanych typów, nie stanowi żadnego problemu przy rozważaniu pierwszego pytania. Obiekty można uznać za równoważne tylko wtedy, gdy ich typy mają wspólną bazę, która określa je jako równoważne; ponieważ Stringi Pointnie mają takiej bazy (ich jedyny wspólny typ bazy uznaje wszystkie odrębne obiekty za równoważne) odpowiedź brzmi po prostu „nie”.

Fakt, że typy nie są ze sobą powiązane, stanowi jednak ogromny problem w odniesieniu do drugiego pytania. Niektóre rodzaje definiować relacje zamawiania wśród swoich wystąpień, a niektóre relacje Zamawiający może nawet rozciągać na wiele typów [np byłoby możliwe BigIntegeri BigDecimalzdefiniowanie metod porównawczych, które pozwoliłyby wystąpienia dowolnego typu, aby zostać sklasyfikowanym w stosunku do wystąpień drugi], ale generalnie nie jest możliwe, aby wziąć dwie arbitralne instancje i zapytać „Czy X posortuje przed, po lub jako równoważnik Y” i uzyskać całkowite uporządkowanie. Można by zapytać „Czy X powinien sortować przed, po, równoważny lub nierankingowany względem Y”, czy obiekty muszą zgłaszać spójne uporządkowanie, ale nie całkowitejeden, ale większość algorytmów sortowania wymaga całkowitego uporządkowania. Zatem nawet jeśli wszystkie obiekty mogłyby zaimplementować compareTometodę, jeśli „nierankowany” był prawidłowym zwrotem, taka metoda nie byłaby na tyle użyteczna, aby uzasadnić jej istnienie.

supercat
źródło