Considere el siguiente fragmento:Mejores prácticas con respecto a iguales: ¿sobrecargar o no sobrecargar?
import java.util.*;
public class EqualsOverload {
public static void main(String[] args) {
class Thing {
final int x;
Thing(int x) { this.x = x; }
public int hashCode() { return x; }
public boolean equals(Thing other) { return this.x == other.x; }
}
List<Thing> myThings = Arrays.asList(new Thing(42));
System.out.println(myThings.contains(new Thing(42))); // prints "false"
}
}
en cuenta que contains
vuelve false
!!! ¡Parece que hemos perdido nuestras cosas!
El fallo, por supuesto, es el hecho de que hemos sobrecargado accidentalmente , en lugar de anulado, Object.equals(Object)
. Si hubiéramos escrito class Thing
de la siguiente manera, entonces contains
devuelve true
como se esperaba.
class Thing {
final int x;
Thing(int x) { this.x = x; }
public int hashCode() { return x; }
@Override public boolean equals(Object o) {
return (o instanceof Thing) && (this.x == ((Thing) o).x);
}
}
Effective Java 2ª edición, artículo 36: utilizar constantemente la anotación de anulación, utiliza esencialmente el mismo argumento para recomendar que @Override
se debe utilizar de forma coherente. Este consejo es bueno, por supuesto, porque si hubiésemos intentado declarar @Override equals(Thing other)
en el primer fragmento, nuestro amigable pequeño compilador señalaría inmediatamente nuestro pequeño y tonto error, ya que es una sobrecarga, no una anulación.
Lo que el libro no cubre específicamente, sin embargo, es si la sobrecarga de equals
es una buena idea para empezar. Esencialmente, hay 3 situaciones: Sólo
- sobrecarga, sin anulación - incorrecto casi con seguridad!
- Esta es esencialmente la primera fragmento anterior
- Override solamente (sin sobrecarga) - una manera de fijar
- Esta es esencialmente la segunda fragmento anterior
- sobrecarga y sobrescribir combo - otra forma de arreglar
La tercera situación se ilustra con el siguiente fragmento:
class Thing {
final int x;
Thing(int x) { this.x = x; }
public int hashCode() { return x; }
public boolean equals(Thing other) { return this.x == other.x; }
@Override public boolean equals(Object o) {
return (o instanceof Thing) && (this.equals((Thing) o));
}
}
Aquí, a pesar de que ahora tenemos 2 equals
método, todavía hay una lógica de igualdad, y se encuentra en la sobrecarga. El @Override
simplemente delega a la sobrecarga.
Así que las preguntas son:
- ¿Cuáles son los pros y los contras de "anular única" vs "sobrecarga & anulación combo"?
- ¿Existe una justificación para sobrecargar
equals
, o es casi seguro que es una mala práctica?
Diría que la mejor práctica es usar eclipse o un IDE similar para generar el método equals, haciendo algunas ediciones al código generado solo cuando sea necesario. –
El enfoque creo que el mejor es la anotación (Eclipse plugin) del Proyecto Lombok @EqualsAndHashCode, que autogenera ambos equals y hashcode usando sus atributos por defecto (pero configurable). Ver http://projectlombok.org/ –
@Tim Bender: El código generado por el eclipse es simplemente terrible. Al menos debes usar algo como 'Objects.equals 'de Guava para que algo sea legible. – maaartinus