Ich habe mich gefragt, wie man equals () am besten für eine Familie von Klassen implementiert, die alle die gleiche Schnittstelle implementieren (und der Client nur mit dieser Schnittstelle arbeiten soll und nie über implementierende Klassen wissen soll).
>Ich habe mir kein konkretes Beispiel ausgedacht, aber es gibt zwei Beispiele im JDK - java.lang.Number und java.lang.CharSequence, die die Entscheidung verdeutlichen:
%Vor%oder mit CharSequence
%Vor%Würdest du im Idealfall wollen, dass die Bewertung wahr ist oder nicht? Beide Typen implementieren dieselbe Datentypschnittstelle und als Client, der mit Numbers-Instanzen (bzw. CharSequences) arbeitet, hätte ich eine einfachere Lebensdauer, wenn equals die Schnittstellentypen anstelle der Implementierungstypen vergleichen würde.
Nun ist dies kein Idealbeispiel, da das JDK die Implementierungstypen der Öffentlichkeit zugänglich macht, aber angenommen, wir müssten die Kompatibilität mit dem, was bereits vorhanden ist, nicht aufrechterhalten - aus der Sicht des Designers: Soll equals check an der Schnittstelle oder ist es besser so wie es ist, gegen die Implementierung zu überprüfen?
Hinweis: Ich verstehe, dass die Überprüfung auf Gleichheit mit einer Schnittstelle sehr schwer sein kann, um tatsächlich in der Praxis korrekt zu implementieren und sogar trickiger zu sein, da auch gleiche Schnittstellen erforderlich sind gib den gleichen hashCode () zurück. Aber das sind nur Hindernisse in der Implementierung, zum Beispiel CharSequence, obwohl die Schnittstelle ziemlich klein ist, alles Notwendige für die Gleichheitsprüfung vorhanden ist, ohne die interne Struktur der Implementierung zu enthüllen (so ist es prinzipiell möglich, richtig zu implementieren, sogar ohne im Voraus über zukünftige Implementierungen wissen). Aber ich interessiere mich mehr für den Design Aspekt, nicht für die tatsächliche Implementierung. Ich würde mich nicht allein dafür entscheiden, wie schwer etwas umzusetzen ist.
Ich würde normalerweise annehmen, dass "ähnliche" Objekte nicht gleich wären - zum Beispiel würde ich nicht erwarten, dass Integer (1) gleich (Long (1)) übergeben würde. Ich kann mir Situationen vorstellen, in denen das vernünftig wäre, aber da die jdk eine API für allgemeine Zwecke sein muss, wäre es nicht möglich, die Annahme zu machen, dass dies immer die richtige Sache wäre.
Wenn Sie eine Art von benutzerdefinierten Objekten haben, wo es sinnvoll ist, ist es vollkommen in Ordnung, eine erweiterte Definition von equals zu implementieren, wenn Sie
Definieren Sie eine abstrakte Klasse, die implements
Ihre Schnittstelle und definiert final equals () / hashCode () Methoden und lassen Sie Ihre Kunden das stattdessen erweitern:
Beachten Sie, dass Sie, indem Sie Ihre Klasse abstract
erstellen, implements
die Schnittstelle angeben können, ohne die Methoden der Schnittstelle zu definieren.
Ihre Kunden müssen immer noch die Methode something()
implementieren, aber alle ihre Instanzen verwenden Ihren Code für equals () / hashCode () (weil Sie diese Methoden final
erstellt haben) .
Der Unterschied zu Ihren Kunden ist:
extends
anstelle des Schlüsselworts implements
(minor) Für was es wert ist, würde ich wahrscheinlich eine Implementierung-spezifische eques-Implementierung (Randnotiz - vergessen Sie nicht Hash-Code zu implementieren ...). Der Schnittstellen-Level equals () belastet die Implementierer der Schnittstelle ziemlich stark - wer sich der speziellen Anforderung bewusst ist oder nicht.
Oft funktioniert die Implementierungsebene gut, da Ihr Client nur mit einer Implementierung arbeitet (dh MyNumberProcessor kann auf jeder beliebigen Nummer funktionieren, aber praktisch nur eine Instanz von Long und möglicherweise nur eine Doppelt). Generika sind ein guter Weg, um sicherzustellen, dass das passiert.
In dem seltenen Fall, wo es darauf ankommt, würde ich den Client wahrscheinlich so entwerfen, dass er die Injektion eines Komparators erlaubt oder - wenn nicht verfügbar - meine Zahlen in eine VarTypeNumber einkapselt.
Ich würde versuchen, meiner Schnittstelle eine weitere equals-Methode hinzuzufügen. Wie wäre es damit:
%Vor%Dies führt nicht zu unerwartetem Verhalten (verschiedene Typen gleich), aber es bleibt die Möglichkeit offen, nach gleichen Werten zu suchen.
Es gibt ein etwas ähnliches Thema in effektivem Java, wo Gleichheit mit instanceof und getClass diskutiert wird. Ich kann mich jedoch nicht an die Artikelnummer erinnern.
Ich würde jede Implementierung von equals, die für zwei Objekte, die nicht denselben konkreten Typ haben, als wahr zurückgeben, als extrem "überraschendes" Verhalten betrachten. Wenn Sie in einer Box arbeiten, in der Sie während der Kompilierung alle möglichen Implementierer der Schnittstelle kennen, können Sie equals erzeugen, die nur mit Interface-Methoden Sinn ergeben, aber das ist keine Realität für API / Framework-Code.
Sie können nicht einmal sicher sein, dass niemand eine Implementierung der Schnittstelle schreiben wird, die seinen internen Zustand verändert, wenn Sie die Methoden aufrufen, die Sie zum Implementieren von equals verwendet haben! Sprechen wir über Verwirrung, eine Gleichheitsüberprüfung, die wahr zurückgibt und sich selbst ungültig macht?
-
Dies ist, was ich als die Frage verstanden habe, was "Überprüfung der Gleichheit mit der Schnittstelle" betrifft:
%Vor%Es ist möglich, die Gleichheit zwischen verschiedenen Klassen zu definieren.
In Ihrem Fall muss der genaue Gleichheitsalgorithmus von der Schnittstelle angegeben werden, so dass jede Klasse, die die Schnittstelle implementiert, diese einhalten muss. Besser noch, da der Algorithmus nur von Informationen abhängt, die von der Schnittstelle zur Verfügung gestellt werden, implementieren Sie sie einfach bereits, so dass Unterklassen sie einfach ausleihen können.
%Vor%