Gibt es ein Java Reflection Utility, um zwei Objekte gründlich zu vergleichen?


99

Ich versuche, Unit-Tests für eine Vielzahl von clone()Operationen in einem großen Projekt zu schreiben, und ich frage mich, ob es irgendwo eine Klasse gibt, die in der Lage ist, zwei Objekte desselben Typs zu nehmen, einen gründlichen Vergleich durchzuführen und zu sagen, ob sie vorhanden sind Sind Sie identisch oder nicht?


1
Wie würde diese Klasse wissen, ob sie an einem bestimmten Punkt der Objektgraphen identische Objekte oder nur dieselben Referenzen akzeptieren kann?
Zed

Im Idealfall ist es konfigurierbar genug :) Ich suche etwas Automatisches, damit der Test sie identifizieren kann, wenn neue Felder hinzugefügt (und nicht geklont) werden.
Uri

3
Ich versuche zu sagen, dass Sie die Vergleiche sowieso konfigurieren (dh implementieren) müssen. Warum also nicht die Methode equals in Ihren Klassen überschreiben und diese verwenden?
Zed

3
Wenn gleich für ein großes komplexes Objekt false zurückgibt, wo fangen Sie an? Sie sind viel besser dran, das Objekt in einen mehrzeiligen String umzuwandeln und einen String-Vergleich durchzuführen. Dann können Sie genau sehen, wo zwei Objekte unterschiedlich sind. IntelliJ öffnet ein Vergleichsfenster "Änderungen", das hilft, mehrere Änderungen zwischen zwei Ergebnissen zu finden, dh es versteht die Ausgabe von assertEquals (string1, string2) und gibt Ihnen ein Vergleichsfenster.
Peter Lawrey

Es gibt hier einige wirklich gute Antworten, außer der akzeptierten, die begraben zu sein scheinen
user1445967

Antworten:


62

Unitils hat diese Funktionalität:

Gleichheitsbehauptung durch Reflexion mit verschiedenen Optionen wie dem Ignorieren von Java-Standard- / Nullwerten und dem Ignorieren der Reihenfolge von Sammlungen


9
Ich habe einige Tests mit dieser Funktion durchgeführt und es scheint einen tiefen Vergleich zu geben, wo dies bei EqualsBuilder nicht der Fall ist.
Howard

Gibt es eine Möglichkeit, vorübergehende Felder nicht zu ignorieren?
Prise

@ Pinch ich höre dich. Ich würde sagen, dass das Deep-Compare-Tool unitilsgenau deshalb fehlerhaft ist, weil es Variablen vergleicht, auch wenn sie möglicherweise keine beobachtbaren Auswirkungen haben . Eine weitere (unerwünschte) Folge des Vergleichs von Variablen ist, dass reine Verschlüsse (ohne eigenen Zustand) nicht unterstützt werden. Außerdem müssen die verglichenen Objekte vom gleichen Laufzeittyp sein. Ich krempelte die Ärmel hoch und erstellte meine eigene Version des Deep-Compare-Tools, mit dem diese Probleme behoben werden.
Beluchin

@ Wolfgang gibt es einen Beispielcode, an den wir weitergeleitet werden können? Woher haben Sie dieses Zitat?
anon58192932

30

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 equalsdavon ist (oder könnte jemals sein) eine echte Gleichheitsbeziehung ist die in java.lang.Object. Selbst wenn Sie equalsalles 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 aLinkedListoder 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!


15

Siehe DeepEquals und DeepHashCode () in java-util: https://github.com/jdereg/java-util

Diese Klasse macht genau das, was der ursprüngliche Autor verlangt.


4
Warnung: DeepEquals verwendet die .equals () -Methode eines Objekts, falls vorhanden. Dies ist möglicherweise nicht das, was Sie wollen.
Adam

4
Es wird nur .equals () für eine Klasse verwendet, wenn explizit eine equals () -Methode hinzugefügt wurde, andernfalls wird ein Vergleich von Mitglied zu Mitglied durchgeführt. Die Logik hier ist, dass, wenn sich jemand die Mühe gemacht hat, eine benutzerdefinierte equals () -Methode zu schreiben, diese verwendet werden sollte. Zukünftige Verbesserung: Lassen Sie das Flag die Methoden equals () ignorieren, auch wenn sie vorhanden sind. In Java-Util gibt es nützliche Dienstprogramme wie CaseInsensitiveMap / Set.
John DeRegnaucourt

Ich mache mir Sorgen um den Vergleich von Feldern. Der Unterschied in den Feldern ist aus Sicht des Clients der Objekte möglicherweise nicht erkennbar, und dennoch würde ein tiefer Vergleich auf der Grundlage von Feldern ihn kennzeichnen. Darüber hinaus müssen zum Vergleichen von Feldern die Objekte vom gleichen Laufzeittyp sein, was möglicherweise einschränkend ist.
Beluchin

Um @beluchin oben zu beantworten, führt DeepEquals.deepEquals () nicht immer einen feldweisen Vergleich durch. Erstens hat es die Option, .equals () für eine Methode zu verwenden, wenn eine vorhanden ist (das ist nicht die für Object), oder es kann ignoriert werden. Zweitens werden beim Vergleichen von Karten / Sammlungen weder der Sammlungs- oder Kartentyp noch die Felder in der Sammlung / Karte berücksichtigt. Stattdessen werden sie logisch verglichen. Eine LinkedHashMap kann einer TreeMap entsprechen, wenn sie denselben Inhalt und dieselben Elemente in derselben Reihenfolge haben. Für nicht geordnete Sammlungen und Karten sind nur Artikel in Größe und Tiefe erforderlich.
John DeRegnaucourt

Beim Vergleich von Karten / Sammlungen werden weder der Sammlungs- oder Kartentyp noch die Felder in der Sammlung / Karte berücksichtigt. Stattdessen werden sie logisch verglichen. @JohnDeRegnaucourt. Ich würde diesen logischen Vergleich argumentieren, dh nur vergleichen, was publicfür alle Typen gelten sollte, anstatt nur für Sammlungen / Karten.
Beluchin

10

Override Die equals () -Methode

Sie können die equals () -Methode der Klasse einfach mit EqualsBuilder.reflectionEquals () überschreiben, wie hier erläutert :

 public boolean equals(Object obj) {
   return EqualsBuilder.reflectionEquals(this, obj);
 }

7

Es musste lediglich ein Vergleich zweier von Hibernate Envers überarbeiteter Entitätsinstanzen implementiert werden. Ich fing an, meinen eigenen Unterschied zu schreiben, fand dann aber den folgenden Rahmen.

https://github.com/SQiShER/java-object-diff

Sie können zwei Objekte desselben Typs vergleichen und es werden Änderungen, Ergänzungen und Entfernungen angezeigt. Wenn es keine Änderungen gibt, sind die Objekte (theoretisch) gleich. Für Getter werden Anmerkungen bereitgestellt, die bei der Überprüfung ignoriert werden sollten. Das Rahmenwerk hat weitaus umfassendere Anwendungen als die Gleichheitsprüfung, dh ich verwende es, um ein Änderungsprotokoll zu erstellen.

Die Leistung ist in Ordnung. Wenn Sie JPA-Entitäten vergleichen, müssen Sie sie zuerst vom Entitätsmanager trennen.


6

Ich benutze XStream:

/**
 * @see java.lang.Object#equals(java.lang.Object)
 */
@Override
public boolean equals(Object o) {
    XStream xstream = new XStream();
    String oxml = xstream.toXML(o);
    String myxml = xstream.toXML(this);

    return myxml.equals(oxml);
}

/**
 * @see java.lang.Object#hashCode()
 */
@Override
public int hashCode() {
    XStream xstream = new XStream();
    String myxml = xstream.toXML(this);
    return myxml.hashCode();
}

5
Andere Sammlungen als Listen geben möglicherweise Elemente in unterschiedlicher Reihenfolge zurück, sodass der Zeichenfolgenvergleich fehlschlägt.
Alexey Berezkin

Auch Klassen, die nicht serialisierbar sind, werden fehlschlagen
Zwelch

6

In AssertJ können Sie Folgendes tun:

Assertions.assertThat(expectedObject).isEqualToComparingFieldByFieldRecursively(actualObject);

Wahrscheinlich wird es nicht in allen Fällen funktionieren, aber es wird in mehr Fällen funktionieren, als Sie denken würden.

In der Dokumentation heißt es:

Stellen Sie sicher, dass das zu testende Objekt (tatsächlich) dem angegebenen Objekt entspricht, basierend auf einem rekursiven Vergleich von Eigenschaft / Feld zu Eigenschaft / Feld (einschließlich geerbter Objekte). Dies kann nützlich sein, wenn die tatsächliche Implementierung nicht zu Ihnen passt. Der rekursive Eigenschafts- / Feldvergleich wird nicht auf Felder mit einer benutzerdefinierten Gleichheitsimplementierung angewendet, dh die überschriebene Gleichheitsmethode wird anstelle eines Feld-für-Feld-Vergleichs verwendet.

Der rekursive Vergleich behandelt Zyklen. Standardmäßig werden Floats mit einer Genauigkeit von 1.0E-6 verglichen und mit 1.0E-15 verdoppelt.

Sie können einen benutzerdefinierten Komparator pro (verschachteltem) Feld oder Typ mit usingComparatorForFields (Comparator, String ...) bzw. usingComparatorForType (Comparator, Class) angeben.

Die zu vergleichenden Objekte können unterschiedlichen Typs sein, müssen jedoch dieselben Eigenschaften / Felder haben. Wenn das tatsächliche Objekt beispielsweise ein Namenszeichenfolgenfeld hat, wird erwartet, dass das andere Objekt auch eines hat. Wenn ein Objekt ein Feld und eine Eigenschaft mit demselben Namen hat, wird der Eigenschaftswert über dem Feld verwendet.


1
isEqualToComparingFieldByFieldRecursivelyist jetzt veraltet. Verwenden Sie assertThat(expectedObject).usingRecursiveComparison().isEqualTo(actualObject);stattdessen :)
dargmuesli

5

http://www.unitils.org/tutorial-reflectionassert.html

public class User {

    private long id;
    private String first;
    private String last;

    public User(long id, String first, String last) {
        this.id = id;
        this.first = first;
        this.last = last;
    }
}
User user1 = new User(1, "John", "Doe");
User user2 = new User(1, "John", "Doe");
assertReflectionEquals(user1, user2);

2
Besonders nützlich, wenn Sie mit generierten Klassen umgehen müssen, bei denen Sie keinen Einfluss auf Gleichheit haben!
Matthias B

1
stackoverflow.com/a/1449051/829755 hat dies bereits erwähnt. Sie sollten diesen Beitrag bearbeitet haben
user829755

1
@ user829755 Auf diese Weise verliere ich Punkte. Also alles über Punktespiele)) Leute mögen es, Credits für ihre Arbeit zu bekommen, ich auch.
Gavenkoa

3

Hamcrest hat den Matcher samePropertyValuesAs . Es basiert jedoch auf der JavaBeans-Konvention (verwendet Getter und Setter). Sollten die zu vergleichenden Objekte keine Getter und Setter für ihre Attribute haben, funktioniert dies nicht.

import static org.hamcrest.beans.SamePropertyValuesAs.samePropertyValuesAs;
import static org.junit.Assert.assertThat;

import org.junit.Test;

public class UserTest {

    @Test
    public void asfd() {
        User user1 = new User(1, "John", "Doe");
        User user2 = new User(1, "John", "Doe");
        assertThat(user1, samePropertyValuesAs(user2)); // all good

        user2 = new User(1, "John", "Do");
        assertThat(user1, samePropertyValuesAs(user2)); // will fail
    }
}

Die User Bean - mit Getter und Setter

public class User {

    private long id;
    private String first;
    private String last;

    public User(long id, String first, String last) {
        this.id = id;
        this.first = first;
        this.last = last;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getFirst() {
        return first;
    }

    public void setFirst(String first) {
        this.first = first;
    }

    public String getLast() {
        return last;
    }

    public void setLast(String last) {
        this.last = last;
    }

}

Dies funktioniert hervorragend, bis Sie ein POJO haben, das eine Lesemethode isFoofür eine BooleanEigenschaft verwendet. Es gibt eine PR, die seit 2016 geöffnet ist, um das Problem zu beheben. github.com/hamcrest/JavaHamcrest/pull/136
Snekse

2

Wenn Ihre Objekte Serializable implementieren, können Sie Folgendes verwenden:

public static boolean deepCompare(Object o1, Object o2) {
    try {
        ByteArrayOutputStream baos1 = new ByteArrayOutputStream();
        ObjectOutputStream oos1 = new ObjectOutputStream(baos1);
        oos1.writeObject(o1);
        oos1.close();

        ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
        ObjectOutputStream oos2 = new ObjectOutputStream(baos2);
        oos2.writeObject(o2);
        oos2.close();

        return Arrays.equals(baos1.toByteArray(), baos2.toByteArray());
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

1

Ihr Beispiel für eine verknüpfte Liste ist nicht so schwer zu handhaben. Während der Code die beiden Objektdiagramme durchläuft, platziert er besuchte Objekte in einem Set oder einer Karte. Vor dem Durchlaufen einer anderen Objektreferenz wird dieser Satz getestet, um festzustellen, ob das Objekt bereits durchlaufen wurde. Wenn ja, müssen Sie nicht weiter gehen.

Ich stimme der oben genannten Person zu, die sagte, eine LinkedList zu verwenden (wie ein Stack, jedoch ohne synchronisierte Methoden, daher ist es schneller). Das Durchlaufen des Objektgraphen mit einem Stapel und die Verwendung der Reflexion zum Abrufen jedes Felds ist die ideale Lösung. Einmal geschrieben, sollten diese "externen" Methoden equals () und "external" hashCode () alle Methoden equals () und hashCode () aufrufen. Nie wieder brauchen Sie eine Kundengleichmethode ().

Ich habe ein bisschen Code geschrieben, der ein vollständiges Objektdiagramm durchläuft, das bei Google Code aufgelistet ist. Siehe json-io (http://code.google.com/p/json-io/). Es serialisiert einen Java-Objektgraphen in JSON und deserialisiert daraus. Es behandelt alle Java-Objekte mit oder ohne öffentliche Konstruktoren, serialisierbar oder nicht serialisierbar usw. Derselbe Traversal-Code ist die Grundlage für die externe Implementierung von "equals ()" und externem "hashcode ()". Übrigens ist der JsonReader / JsonWriter (json-io) normalerweise schneller als der integrierte ObjectInputStream / ObjectOutputStream.

Dieser JsonReader / JsonWriter könnte zum Vergleich verwendet werden, hilft aber nicht mit Hashcode. Wenn Sie einen universellen Hashcode () und equals () möchten, benötigen Sie einen eigenen Code. Ich kann dies möglicherweise mit einem generischen Grafikbesucher durchziehen. Wir werden sehen.

Andere Überlegungen - statische Felder - das ist einfach - sie können übersprungen werden, da alle equals () -Instanzen für statische Felder den gleichen Wert haben würden, da die statischen Felder von allen Instanzen gemeinsam genutzt werden.

Transiente Felder sind eine auswählbare Option. Manchmal möchten Sie vielleicht, dass Transienten andere Male nicht zählen. "Manchmal fühlst du dich wie eine Nuss, manchmal nicht."

Wenn Sie zum json-io-Projekt zurückkehren (für meine anderen Projekte), finden Sie das externe Projekt equals () / hashcode (). Ich habe noch keinen Namen dafür, aber es wird offensichtlich sein.


1

Apache gibt Ihnen etwas, konvertiert beide Objekte in Zeichenfolgen und vergleicht Zeichenfolgen, aber Sie müssen toString () überschreiben

obj1.toString().equals(obj2.toString())

OverSide toString ()

Wenn alle Felder primitive Typen sind:

import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
@Override
public String toString() {return 
ReflectionToStringBuilder.toString(this);}

Wenn Sie nicht primitive Felder und / oder Sammlungen und / oder Karten haben:

// Within class
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
@Override
public String toString() {return 
ReflectionToStringBuilder.toString(this,new 
MultipleRecursiveToStringStyle());}

// New class extended from Apache ToStringStyle
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.util.*;

public class MultipleRecursiveToStringStyle extends ToStringStyle {
private static final int    INFINITE_DEPTH  = -1;

private int                 maxDepth;

private int                 depth;

public MultipleRecursiveToStringStyle() {
    this(INFINITE_DEPTH);
}

public MultipleRecursiveToStringStyle(int maxDepth) {
    setUseShortClassName(true);
    setUseIdentityHashCode(false);

    this.maxDepth = maxDepth;
}

@Override
protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
    if (value.getClass().getName().startsWith("java.lang.")
            || (maxDepth != INFINITE_DEPTH && depth >= maxDepth)) {
        buffer.append(value);
    } else {
        depth++;
        buffer.append(ReflectionToStringBuilder.toString(value, this));
        depth--;
    }
}

@Override
protected void appendDetail(StringBuffer buffer, String fieldName, 
Collection<?> coll) {
    for(Object value: coll){
        if (value.getClass().getName().startsWith("java.lang.")
                || (maxDepth != INFINITE_DEPTH && depth >= maxDepth)) {
            buffer.append(value);
        } else {
            depth++;
            buffer.append(ReflectionToStringBuilder.toString(value, this));
            depth--;
        }
    }
}

@Override
protected void appendDetail(StringBuffer buffer, String fieldName, Map<?, ?> map) {
    for(Map.Entry<?,?> kvEntry: map.entrySet()){
        Object value = kvEntry.getKey();
        if (value.getClass().getName().startsWith("java.lang.")
                || (maxDepth != INFINITE_DEPTH && depth >= maxDepth)) {
            buffer.append(value);
        } else {
            depth++;
            buffer.append(ReflectionToStringBuilder.toString(value, this));
            depth--;
        }
        value = kvEntry.getValue();
        if (value.getClass().getName().startsWith("java.lang.")
                || (maxDepth != INFINITE_DEPTH && depth >= maxDepth)) {
            buffer.append(value);
        } else {
            depth++;
            buffer.append(ReflectionToStringBuilder.toString(value, this));
            depth--;
        }
    }
}}

0

Ich denke, Sie wissen das, aber theoretisch sollten Sie immer .equals überschreiben, um zu behaupten, dass zwei Objekte wirklich gleich sind. Dies würde bedeuten, dass sie die überschriebenen .equals-Methoden für ihre Mitglieder überprüfen.

Aus diesem Grund wird .equals in Object definiert.

Wenn dies konsequent gemacht würde, hätten Sie kein Problem.


2
Das Problem ist, dass ich das Testen für eine große vorhandene Codebasis, die ich nicht geschrieben habe, automatisieren möchte ... :)
Uri

0

Eine Haltegarantie für einen so tiefen Vergleich könnte ein Problem sein. Was soll das Folgende tun? (Wenn Sie einen solchen Komparator implementieren, wäre dies ein guter Komponententest.)

LinkedListNode a = new LinkedListNode();
a.next = a;
LinkedListNode b = new LinkedListNode();
b.next = b;

System.out.println(DeepCompare(a, b));

Hier ist ein anderes:

LinkedListNode c = new LinkedListNode();
LinkedListNode d = new LinkedListNode();
c.next = d;
d.next = c;

System.out.println(DeepCompare(c, d));

Wenn Sie eine neue Frage haben, fragen Sie es bitte durch Klicken Frage stellen Taste. Fügen Sie einen Link zu dieser Frage hinzu, wenn dies zur Bereitstellung des Kontexts beiträgt.
YoungHobbit

@younghobbit: nein das ist keine neue frage. Ein Fragezeichen in einer Antwort macht dieses Flag nicht angemessen. Bitte achten Sie mehr darauf.
Ben Voigt

Daraus: Using an answer instead of a comment to get a longer limit and better formatting.Wenn dies ein Kommentar ist, warum dann den Antwortbereich verwenden? Deshalb habe ich es markiert. nicht wegen der ?. Diese Antwort wurde bereits von einer anderen Person markiert, die den Kommentar nicht hinterlassen hat. Ich habe das gerade in der Überprüfungswarteschlange. Könnte mein schlechtes sein, ich hätte vorsichtiger sein sollen.
YoungHobbit

0

Ich denke, die einfachste Lösung, die von der Ray Hulha- Lösung inspiriert wurde , besteht darin, das Objekt zu serialisieren und dann das Rohergebnis gründlich zu vergleichen.

Die Serialisierung kann entweder Byte, JSON, XML oder einfach toString usw. sein. ToString scheint billiger zu sein. Lombok generiert für uns einen kostenlosen, einfach anpassbaren ToSTring. Siehe Beispiel unten.

@ToString @Getter @Setter
class foo{
    boolean foo1;
    String  foo2;        
    public boolean deepCompare(Object other) { //for cohesiveness
        return other != null && this.toString().equals(other.toString());
    }
}   

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.