Sovrascrivere il metodo java equals () – non funziona?

Mi sono imbattuto in un interessante (e molto frustrante) problema con il metodo equals() oggi che ha causato quello che pensavo fosse una class ben collaudata per causare un crash e causare un bug che mi ha richiesto molto tempo per rintracciarlo.

Solo per completezza, non stavo usando un IDE o un debugger – solo un buon vecchio editor di testi e System.out. Il tempo era molto limitato ed era un progetto scolastico.

Comunque –

Stavo sviluppando un carrello di acquisto di base che potrebbe contenere un object ArrayList di Book . Per implementare i addBook() , removeBook() e hasBook() del carrello, volevo controllare se il Book esistesse già nel Cart . Quindi vado –

 public boolean equals(Book b) { ... // More code here - null checks if (b.getID() == this.getID()) return true; else return false; } 

Tutto funziona bene nei test. Creo 6 oggetti e li compilo con i dati. Fai molte aggiunte, rimuovi, ha () operazioni sul Cart e tutto funziona correttamente. Ho letto che puoi avere equals(TYPE var) o equals(Object o) { (CAST) var } ma ho pensato che dal momento che funzionasse, non importava troppo.

Poi mi sono imbattuto in un problema: avevo bisogno di creare un object Book con solo l’ ID contenuto all’interno della class Book. Nessun altro dato sarebbe inserito in esso. Fondamentalmente il seguente:

 public boolean hasBook(int i) { Book b = new Book(i); return hasBook(b); } public boolean hasBook(Book b) { // .. more code here return this.books.contains(b); } 

All’improvviso, il metodo equals(Book b) non funziona più. Questo ha richiesto molto tempo per rintracciare senza un buon debugger e presupponendo che la class Cart fosse correttamente testata e corretta. Dopo aver swaapping il metodo equals() al seguente:

 public boolean equals(Object o) { Book b = (Book) o; ... // The rest goes here } 

Tutto ha cominciato a funzionare di nuovo. C’è una ragione per cui il metodo ha deciso di non prendere il parametro Book anche se era chiaramente un object Book ? L’unica differenza sembrava essere stata istanziata all’interno della stessa class e riempita solo con un membro di dati. Sono molto molto confuso. Per favore, fai un po ‘di luce?

In Java, il metodo equals() ereditato da Object è:

 public boolean equals(Object other); 

In altre parole, il parametro deve essere di tipo Object .

ArrayList usa il metodo equals corretto, dove si chiamava sempre quello che non sovrascriveva correttamente gli equals di Object .

Non sovrascrivere correttamente il metodo può causare problemi.

I override equivale a quanto segue ogni volta:

 @Override public boolean equals(Object other){ if (other == null) return false; if (other == this) return true; if (!(other instanceof MyClass))return false; MyClass otherMyClass = (MyClass)other; ...test other properties here... } 

L’uso dell’annotazione @Override può aiutare un sacco con errori stupidi.

Usalo ogni volta che pensi di sovrascrivere un metodo di super class o interfaccia. In questo modo, se lo fai male otterrai un errore di compilazione.

Se usi eclipse, vai al menu principale

Origine -> Genera equals () e hashCode ()

Un po ‘fuori tema rispetto alla tua domanda, ma probabilmente vale la pena menzionarlo in ogni caso:

Commons Lang ha alcuni metodi eccellenti che puoi usare in override di equals e hashcode. Controlla EqualsBuilder.reflectionEquals (…) e HashCodeBuilder.reflectionHashCode (…) . Mi ha salvato un sacco di mal di testa in passato – anche se, naturalmente, se si vuole fare “uguali” su ID, potrebbe non adattarsi alle circostanze.

Sono anche d’accordo che dovresti usare l’annotazione @Override ogni volta che stai @Override override degli uguali (o qualsiasi altro metodo).

Un’altra soluzione rapida che consente di salvare il codice boilerplate è l’ annotazione Lombok EqualsAndHashCode . È facile, elegante e personalizzabile. E non dipende dall’IDE . Per esempio;

 import lombok.EqualsAndHashCode; @EqualsAndHashCode(of={"errorNumber","messageCode"}) // Will only use this fields to generate equals. public class ErrorMessage{ private long errorNumber; private int numberOfParameters; private Level loggingLevel; private String messageCode; 

Vedi le opzioni disponibili per personalizzare i campi da utilizzare negli uguali. Lombok è disponibile in versione di prova . Basta aggiungerlo con l’ambito fornito :

  org.projectlombok lombok 1.14.8 provided  

in Android Studio è alt + insert —> equals e hashCode

Esempio:

  @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Proveedor proveedor = (Proveedor) o; return getId() == proveedor.getId(); } @Override public int hashCode() { return getId(); } 

Prendere in considerazione:

 Object obj = new Book(); obj.equals("hi"); // Oh noes! What happens now? Can't call it with a String that isn't a Book... 

l’istruzione instanceOf viene spesso utilizzata nell’implementazione di equals.

Questa è una trappola popolare!

Il problema è che l’uso di instanceOf viola la regola della simmetria:

(object1.equals(object2) == true) se e solo se (object2.equals(object1))

se il primo equals è vero, e object2 è un’istanza di una sottoclass della class a cui appartiene obj1, il secondo equals restituirà false!

se la class considerata a cui appartiene ob1 è dichiarata come finale, allora questo problema non può sorgere, ma in generale, si dovrebbe verificare come segue:

this.getClass() != otherObject.getClass(); in caso contrario, restituisce false, altrimenti verifica i campi da confrontare per l’uguaglianza!

recordId è proprietà dell’object

 @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Nai_record other = (Nai_record) obj; if (recordId == null) { if (other.recordId != null) return false; } else if (!recordId.equals(other.recordId)) return false; return true; }