Come sovrascrivere il metodo equals in Java


108

Sto cercando di sovrascrivere il metodo uguale in Java. Ho una lezionePeople che ha fondamentalmente 2 campi dati namee age. Ora voglio sovrascrivere il equalsmetodo in modo da poter controllare tra 2 oggetti Persone.

Il mio codice è il seguente

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

Ma quando scrivo age.equals(other.age)mi dà un errore poiché il metodo uguale può confrontare solo String e age è Integer.

Soluzione

Ho usato l' ==operatore come suggerito e il mio problema è stato risolto.


3
Ehi, che ne dici di this.age == other.age? :)
denis.solonenko

1
Qual è il tipo di dati per l'età? int OR Integer? Inoltre, quale versione di JDK stai usando?
Manish

2
"come il metodo uguale può confrontare solo String" - Chi ti ha detto che il metodo uguale può confrontare solo String? Il metodo equals appartiene alla classe Object e qualsiasi classe creata avrà un'implementazione uguale per impostazione predefinita. Puoi chiamare uguale su QUALSIASI classe Java
Manish

Risposte:


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 (obj.getClass() != this.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;
        }
    }

Produzione:

correre:

- Subash Adhikari - VS - K falso

- Subash Adhikari - VS - StackOverflow false

- Subash Adhikari - VS - Subash Adhikari vero

- K - VS - StackOverflow false

- K - VS - Subash Adhikari falso

- StackOverflow - VS - Subash Adhikari false

- BUILD SUCCESSFUL (tempo totale: 0 secondi)


7
qual è il hash = 53 * hashmotivo per cui lo stai usando?
kittu

2
L'utilizzo getClass()causerà problemi se la classe viene sottoclasse e viene confrontata con un oggetto della superclasse.
Tuxdude

1
potrebbe essere bcoz 53 è un numero primo , dai un'occhiata a questa risposta stackoverflow.com/a/27609/3425489 , ha commentato scegliendo i numeri inhashCode()
Shantaram Tupe

1
La risposta vincente a questa domanda ha un'eccellente spiegazione del motivo per cui sovrascrivi hashCode () stackoverflow.com/a/27609/1992108
Pegasaurus

7
Considera l'idea di usare if (getClass ()! = Obj.getClass ()) ... piuttosto che usare l' instanceofoperatore o isAssignableFrom. Ciò richiederà una corrispondenza di tipo esatta, piuttosto che una corrispondenza di sottotipo. - Requisito simmetrico. Anche per confrontare Stringo altri tipi di oggetti, puoi usare Objects.equals(this.name,other.name).
YoYo

22

L'introduzione di una nuova firma del metodo che modifica i tipi di parametro è chiamata sovraccarico :

public boolean equals(People other){

Qui Peopleè diverso da Object.

Quando la firma di un metodo rimane identica a quella della sua superclasse, viene chiamata sovrascrittura e l' @Overrideannotazione aiuta a distinguere i due in fase di compilazione:

@Override
public boolean equals(Object other){

Senza vedere la dichiarazione effettiva di age, è difficile dire perché appare l'errore.


18

Non sono sicuro dei dettagli poiché non hai pubblicato l'intero codice, ma:

  • ricordati di sovrascrivere hashCode() pure
  • il equalsmetodo dovrebbe avere Object, noPeople come tipo di argomento. Al momento stai sovraccaricando, non sovrascrivendo, il metodo uguale, che probabilmente non è quello che vuoi, soprattutto dato che controlli il suo tipo in seguito.
  • puoi usare instanceofper verificare che sia un oggetto Persone esif (!(other instanceof People)) { result = false;}
  • equalsviene utilizzato per tutti gli oggetti, ma non per le primitive. Penso che tu intenda che l'età è un int(primitivo), nel qual caso usa ==. Nota che un intero (con una 'I' maiuscola) è un oggetto che dovrebbe essere confrontato con uguale.

Vedi Quali problemi dovrebbero essere considerati quando si sovrascrive equals e hashCode in Java? per ulteriori dettagli.


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
}

12

Articolo 10: Rispettare il contratto generale quando la priorità è uguale

Secondo Effective Java , l'override del equalsmetodo sembra semplice, ma ci sono molti modi per sbagliare e le conseguenze possono essere disastrose. Il modo più semplice per evitare problemi è non sovrascrivere il equalsmetodo, nel qual caso ogni istanza della classe è uguale solo a se stessa. Questa è la cosa giusta da fare se si applica una delle seguenti condizioni:

  • Ogni istanza della classe è intrinsecamente unica . Ciò è vero per classi come Thread che rappresentano entità attive anziché valori. L'implementazione uguale fornita da Object ha esattamente il comportamento corretto per queste classi.

  • Non è necessario che la classe fornisca un test di "uguaglianza logica". Ad esempio, java.util.regex.Pattern avrebbe potuto sovrascrivere equals per verificare se due istanze di Pattern rappresentassero esattamente la stessa espressione regolare, ma i progettisti non pensavano che i client avessero bisogno o volessero questa funzionalità. In queste circostanze, l'implementazione uguale ereditata da Object è l'ideale.

  • Una superclasse ha già sovrascritto uguale e il comportamento della superclasse è appropriato per questa classe. Ad esempio, la maggior parte delle implementazioni Set eredita l'implementazione uguale da AbstractSet, le implementazioni List da AbstractList e le implementazioni Map da AbstractMap.

  • La classe è privata o privata del pacchetto e si è certi che il suo metodo uguale non verrà mai invocato. Se sei estremamente avverso al rischio, puoi sovrascrivere il metodo uguale per assicurarti che non venga richiamato accidentalmente:

Il equalsmetodo implementa una relazione di equivalenza. Ha queste proprietà:

  • Riflessivo: per qualsiasi valore di riferimento non nullo x, x.equals(x)deve restituire true.

  • Simmetrico: per qualsiasi valore di riferimento non nullo xe y, x.equals(y)deve restituire true se e solo se y.equals (x) restituisce true.

  • Transitivo: Per eventuali valori di riferimento non nulli x, y, z, se x.equals(y)ritorni truee y.equals(z)ritorni true, quindi x.equals(z)deve restituire true.

  • Coerente: per qualsiasi valore di riferimento non nullo xe y, più richiami di x.equals(y)devono restituire in modo truecoerente o restituire in modo coerente false, a condizione che non venga modificata alcuna informazione utilizzata nei confronti di uguaglianza.

  • Per qualsiasi valore di riferimento non nullo x, x.equals(null)deve restituire false.

Ecco una ricetta per un metodo uguale di alta qualità:

  1. Utilizza l' ==operatore per verificare se l'argomento è un riferimento a questo oggetto. In tal caso, restituisci true. Questa è solo un'ottimizzazione delle prestazioni, ma che vale la pena fare se il confronto è potenzialmente costoso.

  2. Utilizza l' instanceofoperatore per verificare se l'argomento ha il tipo corretto. In caso contrario, restituisci false. In genere, il tipo corretto è la classe in cui si verifica il metodo. Occasionalmente, è un'interfaccia implementata da questa classe. Utilizzare un'interfaccia se la classe implementa un'interfaccia che perfeziona il contratto di uguaglianza per consentire confronti tra classi che implementano l'interfaccia. Le interfacce di raccolta come Set, List, Map e Map.Entry hanno questa proprietà.

  3. Trasmetti l'argomento al tipo corretto. Poiché questo cast è stato preceduto da un'istanza di test, è garantito il successo.

  4. Per ogni campo "significativo" nella classe, controlla se quel campo dell'argomento corrisponde al campo corrispondente di questo oggetto. Se tutti questi test hanno esito positivo, restituisce true; in caso contrario, restituisce false. Se il tipo nel passaggio 2 è un'interfaccia, è necessario accedere ai campi dell'argomento tramite i metodi dell'interfaccia; se il tipo è una classe, potresti essere in grado di accedere direttamente ai campi, a seconda della loro accessibilità.

  5. Per i campi primitivi il cui tipo non è floato double, utilizzare l' ==operatore per i confronti; per i campi di riferimento degli oggetti, chiama il equalsmetodo ricorsivamente; per i floatcampi, utilizzare il Float.compare(float, float)metodo statico ; e per i doublecampi, usa Double.compare(double, double). Il trattamento speciale dei campi float e doppi è reso necessario dall'esistenza di Float.NaN, -0.0fe dagli analoghi valori doppi; Sebbene sia possibile confrontare i campi floate doublecon i metodi statici Float.equalse Double.equals, ciò comporterebbe l'autoboxing su ogni confronto, con prestazioni scadenti. Per i arraycampi, applica queste linee guida a ciascun elemento. Se ogni elemento in un campo matrice è significativo, utilizzare uno dei Arrays.equalsmetodi.

  6. Alcuni campi di riferimento a oggetti possono legittimamente contenere null. Per evitare la possibilità di a NullPointerException, controllare l'uguaglianza di tali campi utilizzando il metodo statico 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
    
    }

1
Non dimenticare di dire che devi anche eseguire hashCode()l' override . Inoltre nota, dal momento che la scrittura Java7 equals()e hashCode()metodi è diventato molto più semplice utilizzando Objects.equals(), Arrays.equals()e Objects.hashCode(), Arrays.hashCode().
Arnold Schrijver

3
Considera l'idea di utilizzare if (getClass() != obj.getClass()) ...anziché utilizzare l'operatore instanceof. Ciò richiederà una corrispondenza di tipo esatta , piuttosto che una corrispondenza di sottotipo. - Requisito simmetrico.
YoYo

@YoYo è corretto ... l'utilizzo di instanceof potrebbe fallire con la proprietà simmetrica. Se o è una sottoclasse di PhoneNumber come forse PhoneNumberWithExtension, e sostituisce uguale allo stesso modo utilizzando instanceof, allora o.equals (this) fallirebbe il test instanceof mentre PhoneNumber.equals lo passerebbe e restituirebbe true (assumendo che tutti gli altri campi PhoneNumber sono uguali).
ldkronos

5

Dato che immagino agesia del tipo 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

Ciò si tradurrà in un NullPointerExceptionse nameè null.
orien

@orien Non è un grosso problema, forse è nel contratto che namenon viene mai assegnato un nullvalore ...
fortran

@fortran Quindi ... forse non è un grosso problema;)
orien

5

Quando si confrontano oggetti in Java, si effettua un controllo semantico , confrontando il tipo e lo stato di identificazione di degli oggetti con:

  • stesso (stessa istanza)
  • stesso (clone o copia ricostruita)
  • altri oggetti di diverso tipo
  • altri oggetti dello stesso tipo
  • null

Regole:

  • Simmetria :a.equals(b) == b.equals(a)
  • equals()cede sempre trueo false, ma mai a NullpointerException, ClassCastExceptiono qualsiasi altro oggetto lanciabile

Confronto:

  • Controllo del tipo : entrambe le istanze devono essere dello stesso tipo, il che significa che devi confrontare le classi effettive per l'uguaglianza. Questo spesso non è implementato correttamente, quando gli sviluppatori usano instanceofper il confronto dei tipi (che funziona solo finché non ci sono sottoclassi e viola la regola di simmetria quando A extends B -> a instanceof b != b instanceof a).
  • Controllo semantico dello stato di identificazione : assicurarsi di aver compreso con quale stato vengono identificate le istanze. Le persone possono essere identificate dal numero di previdenza sociale, ma non dal colore dei capelli (possono essere tinti), nome (può essere cambiato) o età (cambia continuamente). Solo con gli oggetti valore dovresti confrontare lo stato completo (tutti i campi non transitori), altrimenti controlla solo ciò che identifica l'istanza.

Per la tua Personclasse:

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);
}

Riutilizzabile, classe di utilità generica:

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)));
    }
}

Per la tua Personclasse, utilizzando questa classe di utilità:

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

1

se age è int dovresti usare == se è un oggetto Integer allora puoi usare equals (). È inoltre necessario implementare il metodo hashcode se si sovrascrive uguale a. I dettagli del contratto sono disponibili nel javadoc di Object e anche in varie pagine web.


0

Ecco la soluzione che ho usato di recente:

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);
    }

}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.