Ich habe diesen Artikel gelesen: So schreiben Sie eine Gleichheitsmethode in Java .
Grundsätzlich bietet es eine Lösung für eine equals () -Methode, die Vererbung unterstützt:
Point2D twoD = new Point2D(10, 20);
Point3D threeD = new Point3D(10, 20, 50);
twoD.equals(threeD); // true
threeD.equals(twoD); // true
Aber ist es eine gute Idee? Diese beiden Instanzen scheinen gleich zu sein, können jedoch zwei unterschiedliche Hash-Codes haben. Ist das nicht ein bisschen falsch?
Ich glaube, dass dies besser erreicht werden würde, wenn man stattdessen die Operanden wirft.
java
c#
scala
comparison
Wir s
quelle
quelle
z
kann für einige Anwendungen eine nützliche Konvention sein. Aber es ist eine willkürliche Konvention. Ebenen in Räumen mit 3 oder mehr Dimensionen können beliebige Ausrichtungen haben ... das macht interessante Probleme interessant.Antworten:
Dies sollte nicht gleich sein, weil es die Transitivität bricht . Betrachten Sie diese beiden Ausdrücke:
Da Gleichheit transitiv ist, sollte dies bedeuten, dass der folgende Ausdruck auch wahr ist:
Aber natürlich nicht.
Ihre Vorstellung vom Casting ist also richtig - erwarten Sie, dass Casting in Java einfach das Casting des Referenztyps bedeutet. Was Sie hier wirklich wollen, ist eine Konvertierungsmethode, die
Point2D
aus einemPoint3D
Objekt ein neues Objekt erstellt. Dies würde auch den Ausdruck aussagekräftiger machen:quelle
(10, 20, 50)
gleich(10, 20, 60)
gut. Wir kümmern uns nur um10
und20
.Point2D
eineprojectXYZ()
Methode vorhanden sein, um einePoint3D
Repräsentation von sich selbst bereitzustellen ? Mit anderen Worten, sollten sich Implementierungen gegenseitig kennen?Point2D
scheint einfacher zu sein, da für die Projektion von 2D-Punkten zunächst die Ebene im 3D-Raum definiert werden muss. Wenn der 2D-Punkt seine Ebene kennt, ist er bereits ein 3D-Punkt. Wenn nicht, kann es nicht projizieren. Ich erinnere mich an Abbotts Flatland .Plane3D
Objekt definieren , das eine Ebene im 3D-Raum definiert.lift
Diese Ebene kann eine Methode (2D-> 3D wird angehoben, nicht projiziert) haben, die einPoint2D
und eine Zahl für die "dritte Achse" akzeptiert "- Abstand vom Flugzeug entlang der Flächennormalen. Zur Vereinfachung der Verwendung können Sie die gemeinsamen Ebenen als statische Konstanten definieren, sodass Sie beispielsweise Folgendes tun können:Plane3D.XY.lift(new Point2D(10, 20), 50).equals(new Point3D(10, 20, 50))
Ich entferne mich vom Artikel und denke über die Weisheit von Alan J. Perlis nach:
Die Tatsache, dass die richtige "Gleichheit" ein Problem ist, das den Erfinder von Scala , Martin Ordersky, nachts wach hält, sollte darüber nachdenken, ob das Überschreiben
equals
in einem Vererbungsbaum eine gute Idee ist.Was passiert, wenn wir Pech haben, eine zu erhalten,
ColoredPoint
ist, dass unsere Geometrie fehlschlägt, weil wir die Vererbung verwendet haben, um Datentypen zu vermehren, anstatt einen guten Datentyp zu erstellen. Dies, obwohl Sie zurückgehen und den Stammknoten des Vererbungsbaums ändern müssen, damit erequals
funktioniert. Warum nicht nur hinzufügen ,z
und einecolor
zuPoint
?Der gute Grund wäre, dass
Point
undColoredPoint
in verschiedenen Domänen zu betreiben ... zumindest, wenn diese Domänen nie vermischt werden. Wenn dies der Fall ist, müssen wir es nicht überschreibenequals
. VergleicheColoredPoint
undPoint
Gleichberechtigung sind nur in einem dritten Bereich sinnvoll, in dem sie sich vermischen dürfen. In diesem Fall ist es wahrscheinlich besser, die "Gleichheit" auf diese dritte Domäne zugeschnitten zu haben, als zu versuchen, Gleichheitssemantik von der einen oder der anderen oder beiden nicht vermischten Domänen anzuwenden. Mit anderen Worten, "Gleichheit" sollte lokal an dem Ort definiert werden, an dem Schlamm von beiden Seiten einströmt, weil wir nichtColoredPoint.equals(pt)
gegen Instanzen scheitern wollen ,Point
selbst wenn der Verfasser derColoredPoint
Meinung war, dass dies vor sechs Monaten um 2 Uhr morgens eine gute Idee war .quelle
Als die alten Programmiergötter das objektorientierte Programmieren mit Klassen erfanden, entschieden sie sich, wenn es um Komposition und Vererbung ging, zwei Beziehungen für ein Objekt zu haben: "ist ein" und "hat ein".
Dies löste teilweise das Problem, dass Unterklassen sich von übergeordneten Klassen unterschieden, sie jedoch verwendbar machten, ohne Code zu brechen. Da eine Unterklasseninstanz "ein" Oberklassenobjekt ist und direkt durch ein Objekt ersetzt werden kann, obwohl die Unterklasse mehr Elementfunktionen oder Datenelemente enthält, garantiert "a", dass alle Funktionen des übergeordneten Objekts ausgeführt werden und alle Funktionen vorhanden sind Mitglieder. Man könnte also sagen, ein Point3D ist ein Point, und ein Point2D ist ein Point, wenn beide von Point erben. Zusätzlich kann ein Point3D eine Unterklasse von Point2D sein.
Die Klassengleichheit ist jedoch problemdomänenspezifisch, und das obige Beispiel ist nicht eindeutig, was der Programmierer benötigt, damit das Programm ordnungsgemäß funktioniert. Im Allgemeinen werden Regeln für mathematische Domänen befolgt, und Datenwerte führen zu Gleichheit, wenn Sie den Umfang des Vergleichs auf nur zwei Dimensionen beschränken, aber nicht, wenn Sie alle Datenelemente vergleichen.
So erhalten Sie eine Tabelle mit einschränkenden Gleichungen:
Sie wählen im Allgemeinen die strengsten Regeln, die Sie können, um dennoch alle erforderlichen Funktionen in Ihrer Problemdomäne auszuführen. Die eingebauten Gleichheitstests für Zahlen sind so restriktiv wie möglich für mathematische Zwecke, aber der Programmierer hat viele Möglichkeiten, um dies zu umgehen, wenn dies nicht das Ziel ist, einschließlich Auf- / Abrunden, Abschneiden, gt, lt usw . Objekte mit Zeitstempeln werden häufig nach ihrer Generierungszeit verglichen. Daher muss jede Instanz eindeutig sein, damit Vergleiche sehr spezifisch werden.
In diesem Fall besteht der Entwurfsfaktor darin, effiziente Methoden zum Vergleichen von Objekten zu bestimmen. Manchmal ist ein rekursiver Vergleich aller Objektdatenelemente erforderlich, und dies kann sehr teuer werden, wenn Sie viele, viele Objekte mit vielen Datenelementen haben. Alternativ können Sie nur relevante Datenwerte vergleichen oder das Objekt einen Hash-Wert der betroffenen Datenelemente generieren lassen, um einen schnellen Vergleich mit anderen ähnlichen Objekten zu ermöglichen. Sie können Sammlungen sortieren und bereinigen, um Vergleiche zu beschleunigen und weniger CPU-intensiv zu machen, und möglicherweise Objekte zulassen, die dies zulassen Daten identisch sind, die gekeult werden sollen, und ein doppelter Zeiger auf ein einzelnes Objekt an seine Stelle gesetzt wird.
quelle
Die Regel ist, wann immer Sie überschreiben
hashcode()
, Sie überschreibenequals()
und umgekehrt. Ob dies eine gute Idee ist oder nicht, hängt von der beabsichtigten Verwendung ab. Persönlich würde ich mit einer anderen (isLike()
oder ähnlichen) Methode den gleichen Effekt erzielen.quelle
Es ist oft nützlich , wenn nicht öffentlich zugängliche Klassen über eine Methode zum Testen der Äquivalenz verfügen, mit der Objekte unterschiedlicher Typen einander als "gleich" betrachten können, wenn sie die gleichen Informationen darstellen, weil Java jedoch keine Mittel zulässt, mit denen sich Klassen als solche ausgeben können Zum anderen ist es oft gut, einen einzelnen öffentlich zugänglichen Wrapper-Typ zu haben, wenn es möglich sein könnte, äquivalente Objekte mit unterschiedlichen Darstellungen zu haben.
Angenommen, eine Klasse kapselt eine unveränderliche 2D-Wertematrix
double
. Wenn eine externe Methode nach einer Identitätsmatrix der Größe 1000 fragt, fragt eine zweite nach einer Diagonalmatrix und übergibt ein Array mit 1000 Einsen, und eine dritte fragt nach einer 2D-Matrix und übergibt ein 1000x1000-Array, bei dem alle Elemente auf der primären Diagonale 1,0 sind und alle anderen sind Null, die Objekte, die an alle drei Klassen übergeben werden, können intern unterschiedliche Hintergrundspeicher verwenden [der erste hat ein einzelnes Feld für die Größe, der zweite hat ein Tausend-Elemente-Array und der dritte hat ein Tausend-1000-Elemente-Array], aber sollten sich gegenseitig als äquivalent melden [da alle drei eine unveränderliche 1000x1000-Matrix mit Einsen auf der Diagonale und Nullen überall sonst einkapseln].Abgesehen von der Tatsache, dass das Vorhandensein unterschiedlicher Backing-Store-Typen verborgen ist, ist der Wrapper auch nützlich, um Vergleiche zu erleichtern, da das Überprüfen von Elementen auf Gleichwertigkeit im Allgemeinen ein mehrstufiger Prozess ist. Fragen Sie den ersten Punkt, ob er weiß, ob er dem zweiten entspricht. Wenn es nicht weiß, fragen Sie das zweite, ob es weiß, ob es gleich dem ersten ist. Wenn keines der beiden Objekte dies weiß, fragen Sie jedes Array nach dem Inhalt seiner einzelnen Elemente [möglicherweise werden weitere Überprüfungen hinzugefügt, bevor Sie sich für den langwierigen Vergleich einzelner Elemente entscheiden].
Beachten Sie, dass die Äquivalenztestmethode für jedes Objekt in diesem Szenario einen Wert mit drei Zuständen zurückgeben muss ("Ja, ich bin äquivalent", "Nein, ich bin nicht äquivalent" oder "Ich weiß nicht"). daher wäre die normale "gleich" -Methode nicht geeignet. Während jedes Objekt einfach "Ich weiß nicht" antworten könnte, wenn es nach einem anderen Objekt gefragt wird, würde das Hinzufügen von Logik zu z. B. einer Diagonalmatrix, die keine Identitätsmatrix oder Diagonalmatrix nach Elementen außerhalb der Hauptdiagonale befragt, Vergleiche zwischen solchen Elementen erheblich beschleunigen Typen.
quelle