Ich liebe diese Frage! Hauptsächlich, weil es kaum jemals schlecht oder schlecht beantwortet wird. Es ist, als hätte es noch niemand herausgefunden. Neuland :)
Denken Sie zunächst einmal nicht einmal an die Verwendung equals
. Der Vertrag von equals
, wie im Javadoc definiert, ist eine Äquivalenzbeziehung (reflexiv, symmetrisch und transitiv), keine Gleichheitsbeziehung. Dafür müsste es auch antisymmetrisch sein. Die einzige Implementierung equals
davon ist (oder könnte jemals sein) eine echte Gleichheitsbeziehung ist die in java.lang.Object
. Selbst wenn Sie equals
alles in der Grafik verglichen haben, ist das Risiko eines Vertragsbruchs recht hoch. Wie Josh Bloch in Effective Java betonte, ist der Gleichstellungsvertrag sehr leicht zu brechen:
"Es gibt einfach keine Möglichkeit, eine instanziierbare Klasse zu erweitern und einen Aspekt hinzuzufügen, während der gleichwertige Vertrag erhalten bleibt."
Abgesehen davon, was nützt Ihnen eine boolesche Methode überhaupt? Es wäre schön, tatsächlich alle Unterschiede zwischen dem Original und dem Klon zusammenzufassen, finden Sie nicht? Außerdem gehe ich hier davon aus, dass Sie sich nicht die Mühe machen möchten, Vergleichscode für jedes Objekt in der Grafik zu schreiben / zu pflegen, sondern nach etwas suchen, das mit der Quelle skaliert, wenn es sich im Laufe der Zeit ändert.
Soooo, was Sie wirklich wollen, ist eine Art Zustandsvergleichstool. Wie dieses Tool implementiert wird, hängt wirklich von der Art Ihres Domänenmodells und Ihren Leistungsbeschränkungen ab. Nach meiner Erfahrung gibt es kein generisches Wundermittel. Und es wird über eine große Anzahl von Iterationen langsam sein. Aber um die Vollständigkeit einer Klonoperation zu testen, wird es die Arbeit ziemlich gut machen. Ihre beiden besten Optionen sind Serialisierung und Reflektion.
Einige Probleme, auf die Sie stoßen werden:
- Sammlungsreihenfolge: Sollten zwei Sammlungen als ähnlich angesehen werden, wenn sie dieselben Objekte enthalten, jedoch in einer anderen Reihenfolge?
- Welche Felder sind zu ignorieren: Transient? Statisch?
- Typäquivalenz: Sollten Feldwerte genau vom gleichen Typ sein? Oder ist es in Ordnung, wenn einer den anderen erweitert?
- Es gibt noch mehr, aber ich vergesse ...
XStream ist ziemlich schnell und in Kombination mit XMLUnit erledigt dies die Aufgabe in nur wenigen Codezeilen. XMLUnit ist nett, weil es alle Unterschiede melden kann oder einfach beim ersten Stopp anhält. Und seine Ausgabe enthält den xpath zu den verschiedenen Knoten, was sehr schön ist. Standardmäßig sind keine ungeordneten Sammlungen zulässig, es kann jedoch dafür konfiguriert werden. Durch Injizieren eines speziellen Differenz-Handlers (a genannt DifferenceListener
) können Sie festlegen, wie mit Unterschieden umgegangen werden soll, einschließlich des Ignorierens der Reihenfolge. Sobald Sie jedoch etwas tun möchten, das über die einfachste Anpassung hinausgeht, wird das Schreiben schwierig und die Details sind in der Regel an ein bestimmtes Domänenobjekt gebunden.
Meine persönliche Präferenz ist es, mithilfe der Reflexion alle deklarierten Felder zu durchlaufen und einen Drilldown in die einzelnen Felder durchzuführen, um die Unterschiede zu verfolgen. Warnung: Verwenden Sie keine Rekursion, es sei denn, Sie mögen Stapelüberlauf-Ausnahmen. Halten Sie die Dinge mit einem Stapel im Umfang (verwenden Sie aLinkedList
oder so). Normalerweise ignoriere ich transiente und statische Felder und überspringe Objektpaare, die ich bereits verglichen habe, damit ich nicht in Endlosschleifen gerate, wenn sich jemand dazu entschließt, selbstreferenziellen Code zu schreiben (ich vergleiche jedoch immer primitive Wrapper, egal was passiert , da die gleichen Objektreferenzen häufig wiederverwendet werden). Sie können die Dinge im Voraus so konfigurieren, dass die Sammlungsreihenfolge ignoriert und spezielle Typen oder Felder ignoriert werden. Ich möchte jedoch meine Statusvergleichsrichtlinien für die Felder selbst über Anmerkungen definieren. Dies ist meiner Meinung nach genau das, wofür Annotationen gedacht waren, um Metadaten über die Klasse zur Laufzeit verfügbar zu machen. Etwas wie:
@StatePolicy(unordered=true, ignore=false, exactTypesOnly=true)
private List<StringyThing> _mylist;
Ich denke, das ist tatsächlich ein sehr schweres Problem, aber völlig lösbar! Und wenn Sie etwas haben, das für Sie funktioniert, ist es wirklich sehr, sehr praktisch :)
Also viel Glück. Und wenn Sie sich etwas einfallen lassen, das einfach nur genial ist, vergessen Sie nicht, es zu teilen!