Anulando el método java equals () – ¿no funciona?

Me encontré con un problema interesante (y muy frustrante) con el método equals() hoy, que causó que se bloqueara lo que pensé que era una clase bien probada y causó un error que me llevó mucho tiempo rastrear.

Solo para completar, no estaba usando un IDE o depurador, solo un buen editor de texto antiguo y System.out’s. El tiempo fue muy limitado y fue un proyecto escolar.

De todos modos –

Estaba desarrollando un carrito de compras básico que podría contener un objeto ArrayList of Book . Para implementar los addBook() , removeBook() y hasBook() del carrito, quería verificar si el Book ya existía en el Cart . Así que ya voy –

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

Todo funciona bien en las pruebas. Creo 6 objetos y los llevo con datos. Realiza muchas operaciones de adición, eliminación, has () en el Cart y todo funciona bien. Leí que puede tener equals(TYPE var) o equals(Object o) { (CAST) var } pero asumió que, dado que estaba funcionando, no importaba demasiado.

Luego me encontré con un problema: necesitaba crear un objeto Book con solo la ID dentro de la clase Book. No se ingresarían otros datos. Básicamente lo siguiente:

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

De repente, el método de los equals(Book b) ya no funciona. Esto tomó MUCHO tiempo para rastrear sin un buen depurador y suponiendo que la clase Cart fue correctamente probada y correcta. Después de cambiar el método equals() a lo siguiente:

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

Todo comenzó a funcionar de nuevo. ¿Hay alguna razón por la que el método decidió no tomar el parámetro Book aunque era claramente un objeto Book ? La única diferencia parecía ser que se creaba una instancia dentro de la misma clase, y solo se llenaba con un miembro de datos. Estoy muy confundido. Por favor, arroja algo de luz?

En Java, el método equals() heredado de Object es:

 public boolean equals(Object other); 

En otras palabras, el parámetro debe ser de tipo Object .

ArrayList usa el método equals correcto, donde siempre llamaba al que no anulaba correctamente los equivalentes del objeto.

No anular el método correctamente puede causar problemas.

Anulo es igual a lo siguiente cada vez:

 @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... } 

El uso de la anotación @Override puede ayudar mucho con tontos errores.

Úselo siempre que crea que está anulando una super clase o el método de la interfaz. De esta forma, si lo haces mal, obtendrás un error de comstackción.

Si usa eclipse simplemente vaya al menú superior

Fuente -> Generate equals () y hashCode ()

Un poco fuera del tema de su pregunta, pero probablemente valga la pena mencionarlo de todos modos:

Commons Lang tiene algunos métodos excelentes que puedes usar para anular equals y hashcode. Consulte EqualsBuilder.reflectionEquals (…) y HashCodeBuilder.reflectionHashCode (…) . Me ha ahorrado muchos dolores de cabeza en el pasado, aunque, por supuesto, si solo quieres hacer un “igual” en la identificación, puede que no encaje en tus circunstancias.

También estoy de acuerdo en que debe usar la anotación @Override siempre que esté anulando iguales (o cualquier otro método).

Otra solución rápida que salva el código repetitivo es la anotación de Lombok EqualsAndHashCode . Es fácil, elegante y personalizable. Y no depende del IDE . Por ejemplo;

 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; 

Vea las opciones disponibles para personalizar qué campos usar en los iguales. Lombok está disponible en maven . Solo agréguelo con el scope provisto :

  org.projectlombok lombok 1.14.8 provided  

en Android Studio es alt + insert —> equals y hashCode

Ejemplo:

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

Considerar:

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

la instrucción instanceOf menudo se usa en la implementación de iguales.

¡Este es un escollo popular!

El problema es que el uso de instanceOf viola la regla de simetría:

(object1.equals(object2) == true) if y only if (object2.equals(object1))

si el primer igual es verdadero, y el objeto2 es una instancia de una subclase de la clase a la que pertenece obj1, entonces el segundo igual devolverá falso.

si la clase considerada a la que pertenece ob1 se declara como definitiva, este problema no puede surgir, pero en general, debe probar de la siguiente manera:

this.getClass() != otherObject.getClass(); si no, devuelve falso, de lo contrario prueba los campos para comparar para la igualdad!

recordId es propiedad del objeto

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