So überschreiben Sie die Methode equals in Java

108

Ich versuche, die Methode equals in Java zu überschreiben. Ich habe eine Klasse, Peopledie im Grunde 2 Datenfelder hat nameund age. Jetzt möchte ich die equalsMethode überschreiben , damit ich zwischen 2 Personenobjekten prüfen kann.

Mein Code lautet wie folgt

public boolean equals(People other){
    boolean result;
    if((other == null) || (getClass() != other.getClass())){
        result = false;
    } // end if
    else{
        People otherPeople = (People)other;
        result = name.equals(other.name) &&  age.equals(other.age);
    } // end else

    return result;
} // end equals

Aber wenn ich schreibe age.equals(other.age), gibt es mir einen Fehler, da die Methode equals nur String vergleichen kann und age Integer ist.

Lösung

Ich habe den ==Operator wie vorgeschlagen verwendet und mein Problem ist gelöst.

Biber
quelle
3
Hey, wie wäre es mit this.age == other.age? :)
denis.solonenko
1
Was ist der Datentyp für das Alter? int OR Integer? Welche JDK-Version verwenden Sie?
Manish
2
"as equals method kann nur String vergleichen" - Wer hat dir gesagt, dass equals method nur String vergleichen kann? Die Methode equals gehört zur Object-Klasse, und jede erstellte Klasse wird standardmäßig gleich implementiert. Sie können gleich auf jeder Java-Klasse anrufen
Manish

Antworten:

127
//Written by K@stackoverflow
public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
        ArrayList<Person> people = new ArrayList<Person>();
        people.add(new Person("Subash Adhikari", 28));
        people.add(new Person("K", 28));
        people.add(new Person("StackOverflow", 4));
        people.add(new Person("Subash Adhikari", 28));

        for (int i = 0; i < people.size() - 1; i++) {
            for (int y = i + 1; y <= people.size() - 1; y++) {
                boolean check = people.get(i).equals(people.get(y));

                System.out.println("-- " + people.get(i).getName() + " - VS - " + people.get(y).getName());
                System.out.println(check);
            }
        }
    }
}

//written by K@stackoverflow
public class Person {
    private String name;
    private int age;

    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }

        if (!Person.class.isAssignableFrom(obj.getClass())) {
            return false;
        }

        final Person other = (Person) obj;
        if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
            return false;
        }

        if (this.age != other.age) {
            return false;
        }

        return true;
    }

    @Override
    public int hashCode() {
        int hash = 3;
        hash = 53 * hash + (this.name != null ? this.name.hashCode() : 0);
        hash = 53 * hash + this.age;
        return hash;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Ausgabe:

Lauf:

- Subash Adhikari - VS - K falsch

- Subash Adhikari - VS - StackOverflow false

- Subash Adhikari - VS - Subash Adhikari wahr

- K - VS - StackOverflow false

- K - VS - Subash Adhikari falsch

- StackOverflow - VS - Subash Adhikari false

- ERFOLGREICH BAUEN (Gesamtzeit: 0 Sekunden)

Kim
quelle
7
Was ist der hash = 53 * hashGrund, warum Sie das verwenden?
Kittu
2
Die Verwendung getClass()wird Probleme verursachen, wenn die Klasse in Unterklassen unterteilt und mit einem Objekt der Superklasse verglichen wird.
Tuxdude
1
Vielleicht ist bcoz 53 eine Primzahl. Schauen Sie sich diese Antwort an. stackoverflow.com/a/27609/3425489 , kommentierte er bei der Auswahl der Zahlen inhashCode()
Shantaram Tupe
1
Die gewinnende Antwort auf diese Frage hat eine ausgezeichnete Erklärung, warum Sie hashCode () stackoverflow.com/a/27609/1992108
Pegasaurus
7
Verwenden Sie if (getClass ()! = Obj.getClass ()) ... anstatt den instanceofOperator oder zu verwenden isAssignableFrom. Dies erfordert eine genaue Typübereinstimmung anstelle einer Untertypübereinstimmung. - Symmetrische Anforderung. StringSie können auch andere Objekttypen vergleichen oder verwenden Objects.equals(this.name,other.name).
YoYo
22

Die Einführung einer neuen Methodensignatur, die die Parametertypen ändert, wird als Überladung bezeichnet :

public boolean equals(People other){

Hier Peopleist anders als Object.

Wenn eine Methodensignatur mit der ihrer Oberklasse identisch bleibt, wird sie aufgerufen Überschreiben bezeichnet, und die @OverrideAnnotation hilft beim Kompilieren, die beiden zu unterscheiden:

@Override
public boolean equals(Object other){

Ohne die tatsächliche Erklärung von zu sehen age , ist es schwierig zu sagen, warum der Fehler auftritt.

fortran
quelle
18

Ich bin mir der Details nicht sicher, da Sie nicht den gesamten Code gepostet haben, aber:

  • erinnern außer Kraft zu setzen hashCode()und
  • Die equalsMethode sollte Objectnicht Peopleals Argumenttyp verwendet werden. Im Moment überladen Sie die equals-Methode und überschreiben sie nicht. Dies ist wahrscheinlich nicht das, was Sie wollen, insbesondere, wenn Sie den Typ später überprüfen.
  • Sie können instanceofdamit überprüfen, ob es sich um ein People-Objekt handelt, zif (!(other instanceof People)) { result = false;}
  • equalswird für alle Objekte verwendet, jedoch nicht für Grundelemente. Ich denke du meinst Alter ist ein int(primitives), in diesem Fall nur verwenden== . Beachten Sie, dass eine Ganzzahl (mit einem Großbuchstaben 'I') ein Objekt ist, das mit gleich verglichen werden sollte.

Siehe Welche Probleme sollten beim Überschreiben von equals und hashCode in Java berücksichtigt werden? für mehr Details.

Adrian Mouat
quelle
12
@Override
public boolean equals(Object that){
  if(this == that) return true;//if both of them points the same address in memory

  if(!(that instanceof People)) return false; // if "that" is not a People or a childclass

  People thatPeople = (People)that; // than we can cast it to People safely

  return this.name.equals(thatPeople.name) && this.age == thatPeople.age;// if they have the same name and same age, then the 2 objects are equal unless they're pointing to different memory adresses
}
NeverJr
quelle
12

Punkt 10: Befolgen Sie den allgemeinen Vertrag, wenn Sie das Gleiche überschreiben

Laut Effective Javaequals scheint das Überschreiben der Methode einfach zu sein, aber es gibt viele Möglichkeiten, etwas falsch zu machen, und die Konsequenzen können schwerwiegend sein. Der einfachste Weg, um Probleme zu vermeiden, besteht darin, die equalsMethode nicht zu überschreiben . In diesem Fall ist jede Instanz der Klasse nur für sich selbst gleich. Dies ist das Richtige, wenn eine der folgenden Bedingungen zutrifft:

  • Jede Instanz der Klasse ist von Natur aus eindeutig . Dies gilt für Klassen wie Thread, die aktive Entitäten anstelle von Werten darstellen. Die von Object bereitgestellte gleichwertige Implementierung hat genau das richtige Verhalten für diese Klassen.

  • Es ist nicht erforderlich, dass die Klasse einen „logischen Gleichheitstest“ bereitstellt. Zum Beispiel könnte java.util.regex.Pattern gleich überschrieben werden, um zu überprüfen, ob zwei Musterinstanzen genau denselben regulären Ausdruck darstellen, aber die Designer glaubten nicht, dass Clients diese Funktionalität benötigen oder wollen würden. Unter diesen Umständen ist die von Object geerbte gleichwertige Implementierung ideal.

  • Eine Oberklasse hat bereits gleich überschrieben, und das Verhalten der Oberklasse ist für diese Klasse geeignet. Beispielsweise erben die meisten Set-Implementierungen ihre gleichwertige Implementierung von AbstractSet, List-Implementierungen von AbstractList und Map-Implementierungen von AbstractMap.

  • Die Klasse ist privat oder paketprivat , und Sie sind sicher, dass die Methode equals niemals aufgerufen wird. Wenn Sie extrem risikoavers sind, können Sie die Methode equals überschreiben, um sicherzustellen, dass sie nicht versehentlich aufgerufen wird:

Das equals Methode implementiert eine Äquivalenzbeziehung. Es hat diese Eigenschaften:

  • Reflexiv: Für jeden Referenzwert ungleich Null xgiltx.equals(x) muss true zurückgeben.

  • Symmetrisch: Für alle Nicht-Null-Referenzwerte xund y,x.equals(y) muss wahr zurück , wenn und nur wenn y.equals (x) true zurückgibt.

  • Transitive: Für alle Nicht-Null - Referenzwerte x, y, z, wenn x.equals(y)Erträge trueund y.equals(z)Renditen true, dann x.equals(z)muss zurückkehrentrue .

  • Konsistent: Für alle Nicht-Null-Referenzwerte xund ymüssen mehrere Aufrufe von x.equals(y)konsistent trueoder konsistent zurückgegeben werdenfalse , sofern keine Informationen geändert werden, die in Vergleichen gleich verwendet werden.

  • Für jeden Nicht-Null - Referenzwert x, x.equals(null)zurückgeben muss false.

Hier ist ein Rezept für eine qualitativ hochwertige Gleichstellungsmethode:

  1. Verwenden Sie den ==Operator, um zu überprüfen, ob das Argument auf dieses Objekt verweist. Wenn ja, geben Sie true zurück. Dies ist nur eine Leistungsoptimierung, die sich jedoch lohnt, wenn der Vergleich möglicherweise teuer ist.

  2. Verwenden Sie den instanceofOperator, um zu überprüfen, ob das Argument den richtigen Typ hat. Wenn nicht, geben Sie false zurück. In der Regel ist der richtige Typ die Klasse, in der die Methode auftritt. Gelegentlich handelt es sich um eine von dieser Klasse implementierte Schnittstelle. Verwenden Sie eine Schnittstelle, wenn die Klasse eine Schnittstelle implementiert, die den Gleichheitsvertrag verfeinert, um Vergleiche zwischen Klassen zu ermöglichen, die die Schnittstelle implementieren. Sammlungsschnittstellen wie Set, List, Map und Map.Entry verfügen über diese Eigenschaft.

  3. Verwandeln Sie das Argument in den richtigen Typ. Da dieser Besetzung eine Testinstanz vorausging, ist der Erfolg garantiert.

  4. Überprüfen Sie für jedes "signifikante" Feld in der Klasse, ob dieses Feld des Arguments mit dem entsprechenden Feld dieses Objekts übereinstimmt. Wenn alle diese Tests erfolgreich sind, geben Sie true zurück. Andernfalls geben Sie false zurück. Wenn der Typ in Schritt 2 eine Schnittstelle ist, müssen Sie über Schnittstellenmethoden auf die Felder des Arguments zugreifen. Wenn der Typ eine Klasse ist, können Sie möglicherweise direkt auf die Felder zugreifen, abhängig von ihrer Zugänglichkeit.

  5. Verwenden Sie für primitive Felder, deren Typ nicht floatoder ist double, den ==Operator für Vergleiche. Rufen Sie für Objektreferenzfelder die equalsMethode rekursiv auf. floatVerwenden Sie für Felder die statische Float.compare(float, float)Methode. und für doubleFelder verwenden Double.compare(double, double). Die spezielle Behandlung der Schwimmer und Doppel Felder sind durch die Existenz Float.NaN, -0.0fund die analogen Doppelwerte; Während Sie floatund doubleFelder mit den statischen Methoden Float.equalsund vergleichen könnten Double.equals, würde dies bei jedem Vergleich ein Autoboxing bedeuten, was eine schlechte Leistung hätte. Wenden Sie für arrayFelder diese Richtlinien auf jedes Element an. Wenn jedes Element in einem Arrayfeld von Bedeutung ist, verwenden Sie eine der Arrays.equalsMethoden.

  6. Einige Objektreferenzfelder können rechtmäßig enthalten null. Um die Möglichkeit eines zu vermeiden NullPointerException, überprüfen Sie solche Felder mit der statischen Methode auf Gleichheit Objects.equals(Object, Object).

    // Class with a typical equals method
    
    public final class PhoneNumber {
    
        private final short areaCode, prefix, lineNum;
    
        public PhoneNumber(int areaCode, int prefix, int lineNum) {
    
            this.areaCode = rangeCheck(areaCode,  999, "area code");
    
            this.prefix   = rangeCheck(prefix,    999, "prefix");
    
            this.lineNum  = rangeCheck(lineNum,  9999, "line num");
    
        }
    
        private static short rangeCheck(int val, int max, String arg) {
    
            if (val < 0 || val > max)
    
               throw new IllegalArgumentException(arg + ": " + val);
    
            return (short) val;
    
        }
    
        @Override public boolean equals(Object o) {
            if (o == this)
                return true;
            if (!(o instanceof PhoneNumber))
                return false;
            PhoneNumber pn = (PhoneNumber)o;
            return pn.lineNum == lineNum && pn.prefix == prefix
                    && pn.areaCode == areaCode;
        }
        ... // Remainder omitted
    
    }

quelle
1
Vergessen Sie nicht zu erwähnen, dass Sie auch überschreiben müssen hashCode(). Auch zur Kenntnis, dass seit Java7 Schreiben equals()und hashCode()Methoden ist wesentlich einfacher geworden durch den Einsatz Objects.equals(), Arrays.equals()und Objects.hashCode(), Arrays.hashCode().
Arnold Schrijver
3
Erwägen Sie die if (getClass() != obj.getClass()) ...Verwendung des Operators instanceof , anstatt ihn zu verwenden. Dies erfordert eine genaue Typübereinstimmung anstelle einer Untertypübereinstimmung. - Symmetrische Anforderung.
YoYo
@YoYo ist korrekt ... mit instanceof kann die symmetrische Eigenschaft fehlschlagen. Wenn o eine Unterklasse von PhoneNumber ist, wie z. B. PhoneNumberWithExtension, und diese auf die gleiche Weise überschreibt, indem instanceof verwendet wird, würde o.equals (this) die Testinstanz nicht bestehen, während PhoneNumber.equals sie bestehen und true zurückgeben würde (unter der Annahme aller anderen PhoneNumber-Felder) sind gleich).
ldkronos
5

Da ich vermute, ageist vom Typ int:

public boolean equals(Object other){
    boolean result;
    if((other == null) || (getClass() != other.getClass())){
        result = false;
    } // end if
    else{
        People otherPeople = (People)other;
        result = name.equals(otherPeople.name) &&  age == otherPeople.age;
    } // end else

    return result;
} // end equals
Luchian Grigore
quelle
Dies führt zu einem NullPointerExceptionWenn nameist null.
Orien
@orien Keine große Sache, vielleicht ist es im Vertrag, dass namenie ein nullWert zugewiesen wird ...
fortran
@fortran Also ... vielleicht ist es keine große Sache;)
Orien
5

Beim Vergleichen von Objekten in Java führen Sie eine semantische Prüfung durch , indem Sie den Typ und den Identifizierungsstatus der Objekte vergleichen mit:

  • selbst (gleiche Instanz)
  • selbst (Klon oder rekonstruierte Kopie)
  • andere Objekte unterschiedlicher Art
  • andere Objekte des gleichen Typs
  • null

Regeln:

  • Symmetrie :a.equals(b) == b.equals(a)
  • equals()immer ergibt trueoder false, aber nie ein NullpointerException, ClassCastExceptionoder jede andere throwable

Vergleich:

  • Typprüfung : Beide Instanzen müssen von der zu gleichen Typ, was bedeutet , Sie die aktuellen Klassen für Gleichheit vergleichen haben. Dies wird häufig nicht korrekt implementiert, wenn Entwickler instanceoffür den Typvergleich verwenden (was nur funktioniert, solange keine Unterklassen vorhanden sind, und die Symmetrieregel verletzt, wenn A extends B -> a instanceof b != b instanceof a).
  • Semantische Überprüfung des Identifizierungsstatus : Stellen Sie sicher, dass Sie verstehen, anhand welchem ​​Status die Instanzen identifiziert werden. Personen können anhand ihrer Sozialversicherungsnummer identifiziert werden, jedoch nicht anhand der Haarfarbe (kann gefärbt werden), des Namens (kann geändert werden) oder des Alters (kann ständig geändert werden). Nur mit Wertobjekten sollten Sie den vollständigen Status (alle nicht vorübergehenden Felder) vergleichen, andernfalls nur überprüfen, was die Instanz identifiziert.

Für Ihre PersonKlasse:

public boolean equals(Object obj) {

    // same instance
    if (obj == this) {
        return true;
    }
    // null
    if (obj == null) {
        return false;
    }
    // type
    if (!getClass().equals(obj.getClass())) {
        return false;
    }
    // cast and compare state
    Person other = (Person) obj;
    return Objects.equals(name, other.name) && Objects.equals(age, other.age);
}

Wiederverwendbare generische Dienstprogrammklasse:

public final class Equals {

    private Equals() {
        // private constructor, no instances allowed
    }

    /**
     * Convenience equals implementation, does the object equality, null and type checking, and comparison of the identifying state
     *
     * @param instance       object instance (where the equals() is implemented)
     * @param other          other instance to compare to
     * @param stateAccessors stateAccessors for state to compare, optional
     * @param <T>            instance type
     * @return true when equals, false otherwise
     */
    public static <T> boolean as(T instance, Object other, Function<? super T, Object>... stateAccessors) {
        if (instance == null) {
            return other == null;
        }
        if (instance == other) {
            return true;
        }
        if (other == null) {
            return false;
        }
        if (!instance.getClass().equals(other.getClass())) {
            return false;
        }
        if (stateAccessors == null) {
            return true;
        }
        return Stream.of(stateAccessors).allMatch(s -> Objects.equals(s.apply(instance), s.apply((T) other)));
    }
}

PersonVerwenden Sie für Ihre Klasse diese Dienstprogrammklasse:

public boolean equals(Object obj) {
    return Equals.as(this, obj, t -> t.name, t -> t.age);
}
Peter Walser
quelle
1

Wenn age int ist, sollten Sie == verwenden. Wenn es sich um ein Integer-Objekt handelt, können Sie equals () verwenden. Sie müssen auch die Hashcode-Methode implementieren, wenn Sie equals überschreiben. Einzelheiten zum Vertrag finden Sie im Javadoc von Object sowie auf verschiedenen Seiten im Internet.

Ashwinee K Jha
quelle
0

Hier ist die Lösung, die ich kürzlich verwendet habe:

public class Test {
    public String a;
    public long b;
    public Date c;
    public String d;
    
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof Test)) {
            return false;
        }
        Test testOther = (Test) obj;
        return (a != null ? a.equals(testOther.a) : testOther.a == null)
                && (b == testOther.b)
                && (c != null ? c.equals(testOther.c) : testOther.c == null)
                && (d != null ? d.equals(testOther.d) : testOther.d == null);
    }

}
SSharma
quelle