Warum befindet sich .compareTo () in einer Schnittstelle, während .equals () in Java in einer Klasse enthalten ist?

30

Ich möchte wissen, warum das .compareTo()in der ComparableSchnittstelle ist, während eine Methode wie .equalsin der ObjectKlasse ist. Mir erscheint es willkürlich, warum eine Methode wie diese .compareTo()nicht schon in der ObjectKlasse ist.

Zur Verwendung .compareTo()implementieren Sie die ComparableSchnittstelle und implementieren die .compareTo()Methode für Ihre Zwecke. Für die .equals()Methode überschreiben Sie einfach die Methode in Ihrer Klasse, da alle Klassen von der ObjectKlasse erben .

Meine Frage ist, warum ist eine Methode wie .compareTo()in einer Schnittstelle, die Sie implementieren, und nicht in einer Klasse wie Object? Ebenso, warum soll die .equals()Methode in der Klasse Objectund nicht in irgendeiner Schnittstelle implementiert werden?

Wesley
quelle
2
Es ist eine Design-Wahl der Java-Sprache (bedeutet nicht unbedingt, dass es die richtige Wahl war). In anderen Sprachen, z. B. Haskell , müssen Sie die Gleichheitsschnittstelle implementieren , um die EqWertgleichheit zu erhalten (tatsächlich stellen Sie der Typenklasse eine Instanz zur Verfügung ).
mucaho
2
Verwandte Meta-Frage: Sind diese Fragen doppelt vorhanden?

Antworten:

58

Nicht alle Objekte können verglichen werden, aber alle Objekte können auf Gleichheit überprüft werden. Wenn nichts anderes, kann man sehen, ob zwei Objekte an derselben Stelle im Speicher existieren (Referenzgleichheit).

Was bedeutet das für compareTo()zwei ThreadObjekte? Wie ist ein Thread "größer" als ein anderer? Wie vergleicht man zwei ArrayList<T>s?

Der ObjectVertrag gilt für alle Java-Klassen. Wenn nicht einmal eine Klasse mit anderen Instanzen ihrer eigenen Klasse verglichen werden Objectkann, muss sie nicht Teil der Schnittstelle sein.

Joshua Bloch verwendet die Schlüsselwörter "natürliche Reihenfolge", wenn er erklärt, warum eine Klasse möglicherweise implementieren möchte Comparable. Nicht jede Klasse hat eine natürliche Reihenfolge, wie ich oben in meinen Beispielen erwähnt habe, daher sollte nicht jede Klasse die Methode implementieren Comparableoder Objecthaben compareTo.

... die compareToMethode ist nicht in deklariert Object. ... Es hat einen ähnlichen Charakter wie Objectdie equalsMethode von 's , nur dass es neben einfachen Gleichheitsvergleichen auch Ordnungsvergleiche zulässt und generisch ist. Durch die Implementierung Comparablegibt eine Klasse an, dass ihre Instanzen eine natürliche Reihenfolge haben .

Effektives Java, Zweite Ausgabe : Joshua Bloch. Punkt 12, Seite 62. Ellipsen entfernen Verweise auf andere Kapitel und Codebeispiele.

Für Fälle , in denen Sie haben eine Bestellung auf nicht aufzwingen wollen ComparableKlasse , die keine natürliche Ordnung hat, können Sie immer eine Versorgung ComparatorInstanz Hilfe sortieren sie.


quelle
3
Es macht noch mehr Spaß, wenn Sie über compareTo with Exception nachdenken (das ist keine abstrakte Klasse und hätte sie daher implementiert, was bedeutet, dass aNullPointerException.compareTo (anUnsupportedFlavorException) ... bedeutet?
10
Alle Objekte können auf Gleichheit geprüft werden. In Java ja, aber generell nein. Es gibt einige Beispiele für Objekte, bei denen Gleichheitsvergleiche keinen Sinn ergeben (nicht einmal Referenzgleichheit) - z. B. Singletons. Es kann Schnittstellen (abstrakte Klassen) wie ValueEquality und ReferenceEquality geben. Es könnte nicht einmal so schlimm sein ...
qbd
5
"Alle Objekte können auf Gleichheit geprüft werden. Wenn nichts anderes, kann man sehen, ob zwei Objekte an derselben Stelle im Speicher existieren (Referenzgleichheit)." - da wir ==für letztere haben, hat dies einen hohlen Ring. Ohne Berücksichtigung der redundanten Standardeinstellung kann man gültige Gründe finden, die nicht für equalsalle Klassen gelten, da möglicherweise nicht alle Typen eine Äquivalenzbeziehung unterstützen.
Raphael
3
Zwei Beispiele für Typen, bei denen es nicht sinnvoll ist, Gleichheit zu definieren: Streams (z. B. verzögerte potenziell unendliche Listen oder Zahlen mit unendlicher Genauigkeit) und Funktionen. Ersteres hat das Problem, dass die Herstellung der Gleichheit einen ständigen Vergleich erfordert. Die Entscheidung, ob zwei Funktionen gleich sind, ist nicht zu treffen. Die Abfrage, ob zwei Instanzen dieser Typen am selben Speicherort vorhanden sind, ist 1) nicht sehr nützlich und 2) ermöglicht es Clients, Code zu schreiben, der die Implementierungsdetails berücksichtigt. Heute kann ich Ihnen jedes Mal, wenn Sie danach fragen, eine neue Instanz derselben unendlichen Liste geben. Morgen kann ich es mir merken.
Doval
6
@Snowman Die Tatsache, dass es nutzlos ist, kombiniert mit der Tatsache, dass Implementierungsdetails offen gelegt werden, ist Grund genug, dies nicht zuzulassen. Nahezu jede "wertebasierte" Klasse in Java 8 enthält eine Kurzbezeichnung mit der Aufschrift "Wir sind nicht für das verantwortlich, was passiert, wenn Sie verwenden ==", da die Instanziierung dieser Klassen ein Implementierungsdetail darstellt, die Sprache es jedoch unmöglich macht, sie zu verbergen. Man könnte sagen, dass jeder, der zwei Integers anhand von Referenzen vergleicht, ein Idiot ist, aber es ist immer noch langweilig, den Vergleich zuzulassen.
Doval
8

Das JLS §4.3.2 definiert das classObjekt folgendermaßen:

4.3.2. Das Klassenobjekt

Die Klasse Objectist eine Oberklasse (§8.1.4) aller anderen Klassen.

Alle Klassen- und Array-Typen erben (§8.4.8) die Methoden der Klasse Object, die wie folgt zusammengefasst werden:

  • Die Methode clonewird verwendet, um ein Objekt zu duplizieren.

  • Die Methode equalsdefiniert einen Begriff der Objektgleichheit, der auf dem Wert, nicht auf dem Verweis, dem Vergleich basiert.

  • Die Methode finalizewird unmittelbar vor dem Zerstören eines Objekts ausgeführt (§12.6).

  • Die Methode getClassgibt das Class-Objekt zurück, das die Klasse des Objekts darstellt.

  • ClassFür jeden Referenztyp existiert ein Objekt. Es kann zum Beispiel verwendet werden, um den vollständig qualifizierten Namen einer Klasse, ihrer Mitglieder, ihrer unmittelbaren Superklasse und aller von ihr implementierten Schnittstellen zu ermitteln.

    Der Typ eines Methodenaufrufausdrucks von getClassist, Class<? extends |T|>wo Tdie gesuchte Klasse oder Schnittstelle (§15.12.1) ist getClass.

    Eine deklarierte synchronizedKlassenmethode (§8.4.3.6) wird auf dem Monitor synchronisiert, der dem Klassenobjekt der Klasse zugeordnet ist.

  • Die Methode hashCodeist sehr nützlich, zusammen mit der Methode equals, in Hashtabellen wie java.util.Hashmap.

  • Die Methoden wait, notifyund notifyAllwerden bei der gleichzeitigen Programmierung mithilfe von Threads verwendet (§17.2).

  • Die Methode toStringgibt eine String-Darstellung des Objekts zurück.

Also, das ist, warum equalsin ist , Objectaber compareToin einer separaten Schnittstelle. Ich würde spekulieren, dass sie Objectso gering wie möglich halten wollten . Sie dachten wahrscheinlich, dass fast alle Objects benötigen würden equalsund hashCode(was eigentlich nur eine Form der Gleichheitsprüfung ist), aber nicht alle Objekte müssten ein Konzept der Ordnung haben , wofür compareToes verwendet wird.

durron597
quelle
Ich denke, es könnte theoretisch ein Interface geben, Equitable<T>aber wenn es Objectimplementiert wäre, wäre jede Klasse ein Equitable<Object>. Gibt es an diesem Punkt einen Unterschied? In der Tat nicht wirklich, in der Komplexität ja.
Captain Man
1
@CaptainMan In .Net objecthat Equals(object), genau wie in Java, aber es gibt auch die IEquatable<T>Schnittstelle. Obwohl der Hauptgrund dafür ist, dass Boxing vermieden wird, wenn Tes sich um einen Werttyp handelt, der in Java nicht möglich ist.
SVICK
hashCode ist keine Form der Gleichheitsprüfung, da es Hash-Kollisionen gibt. Wenn A und B gleich sind, haben sie denselben Hashcode, aber wenn A und B denselben Hashcode haben, bedeutet dies nicht, dass sie gleich sind!
Josef
Tatsächlich würde Ihre Antwort stark davon profitieren, auf ein älteres JLS ( titanium.cs.berkeley.edu/doc/java-langspec-1.0.pdf ) umzusteigen - es enthält ein viel besseres Zitat darüber, warum direkt equalsdeklariert Objectwird: The methods equals and hashCode are declared for the benefit of hashtables such as java.util.Hashtable (§21.7)- als Java-Design Da Java 1.0 abwärtskompatibel ist, ist die Auswahl des Java 1.0-Designs der eigentliche Grund dafür equals, wo es sich befindet.
Vaxquis
seit dem Bearbeiten ich wahrscheinlich als „zu drastisch“ wird erhalten abgelehnt, für den Fall vorgeschlagen , sind Sie einfach Strg + C / Strg + V die relevanten Sachen in Ihrer Antwort wollen würde: pastebin.com/8c4EpLRX
vaxquis
2

Denken Sie neben der hervorragenden Antwort von Snowman daran, dass es sich Comparableseit langem um eine allgemeine Benutzeroberfläche handelt. Ein Typ implementiert nicht compareTo(object), er implementiert, compareTo(T)wo Tsein eigener Typ ist. Dies kann nicht implementiert werden object, da objectdie Klasse, die daraus abgeleitet wird, nicht bekannt ist.

objecthätte eine compareTo(object)Methode definieren können , aber dies hätte nicht nur erlaubt, was Snowman hervorhebt, einen Vergleich zwischen zwei ArrayList<T>s oder zwischen zwei Threads, sondern sogar einen Vergleich zwischen einem ArrayList<T>und einem Thread. Das ist noch unsinniger.

hvd
quelle
0

Angenommen, ich habe zwei Objektreferenzen: X gibt eine Instanz an String, in der der Inhalt "George" gespeichert ist. Y kennzeichnet eine Instanz zum PointHalten der Koordinaten [12,34]. Betrachten Sie die folgenden zwei Fragen:

  • Identifizieren X und Y gleichwertige Objekte?

  • Soll X vor, nach oder gleich Y sortieren?

Die Tatsache, dass X und Y Instanzen von nicht verwandten Typen identifizieren, ist bei der Prüfung der ersten Frage kein Problem. Objekte können nur dann als äquivalent betrachtet werden, wenn ihre Typen eine gemeinsame Basis haben, die sie als äquivalent definiert. da Stringund Pointhat keine solche Basis (ihr einziger gemeinsamer Bezug auf Basistyp alle unterschiedlichen Objekte als nicht-äquivalent) , die Antwort ist einfach „nein“.

Die Tatsache, dass die Typen nicht miteinander verwandt sind, wirft jedoch ein großes Problem in Bezug auf die zweite Frage auf. Einige Typen definieren Ordnungsbeziehungen zwischen ihren Instanzen, und einige Ordnungsbeziehungen können sich sogar über mehrere Typen erstrecken [z. B. wäre es möglich BigIntegerund möglich BigDecimal, Vergleichsmethoden zu definieren, die es ermöglichen würden, Instanzen eines Typs in Bezug auf Instanzen des anderen zu ordnen ], aber Es ist im Allgemeinen nicht möglich, zwei willkürliche Instanzen zu nehmen und "Soll X vor, nach oder gleich Y sortieren" zu fragen und eine Gesamtreihenfolge abzuleiten. Es wäre möglich, "Sollte X vor, nach, gleichwertig oder in Bezug auf Y unranked sortieren" zu fragen, ob Objekte eine konsistente Reihenfolge, jedoch keine Gesamtsumme , melden müssenein, aber die meisten Sortieralgorithmen erfordern eine vollständige Reihenfolge. Selbst wenn alle Objekte eine compareToMethode implementieren könnten , wenn "unranked" eine gültige Rückgabe wäre, wäre eine solche Methode nicht nützlich genug, um ihre Existenz zu rechtfertigen.

Superkatze
quelle