¿Qué problemas / dificultades deben considerarse al anular equals
y hashCode
?
fuente
¿Qué problemas / dificultades deben considerarse al anular equals
y hashCode
?
equals()
( javadoc ) debe definir una relación de equivalencia (debe ser reflexiva , simétrica y transitiva ). Además, debe ser coherente (si los objetos no se modifican, debe seguir devolviendo el mismo valor). Además, o.equals(null)
siempre debe devolver falso.
hashCode()
( javadoc ) también debe ser coherente (si el objeto no se modifica en términos de equals()
, debe seguir devolviendo el mismo valor).
La relación entre los dos métodos es:
Cada vez
a.equals(b)
, entoncesa.hashCode()
debe ser igual queb.hashCode()
.
Si anula uno, debe anular el otro.
Use el mismo conjunto de campos que usa para calcular equals()
para calcular hashCode()
.
Utilice las excelentes clases auxiliares EqualsBuilder y HashCodeBuilder de la biblioteca Lang de Apache Commons . Un ejemplo:
public class Person {
private String name;
private int age;
// ...
@Override
public int hashCode() {
return new HashCodeBuilder(17, 31). // two randomly chosen prime numbers
// if deriving: appendSuper(super.hashCode()).
append(name).
append(age).
toHashCode();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Person))
return false;
if (obj == this)
return true;
Person rhs = (Person) obj;
return new EqualsBuilder().
// if deriving: appendSuper(super.equals(obj)).
append(name, rhs.name).
append(age, rhs.age).
isEquals();
}
}
Cuando utilice una colección o mapa basado en hash , como HashSet , LinkedHashSet , HashMap , Hashtable o WeakHashMap , asegúrese de que el hashCode () de los objetos clave que coloca en la colección nunca cambie mientras el objeto está en la colección. La forma a prueba de balas para garantizar esto es hacer que sus claves sean inmutables, lo que también tiene otros beneficios .
instanceof
devuelve falso si su primer operando es nulo (Java efectivo nuevamente).Hay algunos problemas que vale la pena tener en cuenta si se trata de clases que persisten utilizando un Mapeador de relaciones de objetos (ORM) como Hibernate, ¡si no creía que esto ya era excesivamente complicado!
Los objetos con carga lenta son subclases
Si sus objetos persisten usando un ORM, en muchos casos estará tratando con proxys dinámicos para evitar cargar objetos demasiado pronto desde el almacén de datos. Estos proxies se implementan como subclases de su propia clase. Esto significa que
this.getClass() == o.getClass()
volveráfalse
. Por ejemplo:Si se trata de un ORM, usar
o instanceof Person
es lo único que se comportará correctamente.Los objetos cargados perezosos tienen campos nulos
Los ORM generalmente usan los captadores para forzar la carga de objetos con carga lenta. Esto significa que
person.name
seránull
siperson
está cargado de forma diferida, incluso siperson.getName()
fuerza la carga y devuelve "John Doe". En mi experiencia, esto surge más a menudo enhashCode()
yequals()
.Si se trata de un ORM, asegúrese de usar siempre getters y nunca coloque referencias en
hashCode()
yequals()
.Guardar un objeto cambiará su estado
Los objetos persistentes a menudo usan un
id
campo para mantener la clave del objeto. Este campo se actualizará automáticamente cuando se guarde un objeto por primera vez. No use un campo de identificación enhashCode()
. Pero puedes usarloequals()
.Un patrón que uso a menudo es
Pero: no puedes incluir
getId()
enhashCode()
. Si lo hace, cuando un objeto persiste, sushashCode
cambios. Si el objeto está en aHashSet
, "nunca" lo encontrará de nuevo.En mi
Person
ejemplo, probablemente usaríagetName()
forhashCode
ygetId()
plusgetName()
(solo para paranoia) paraequals()
. Está bien si hay algún riesgo de "colisiones"hashCode()
, pero nunca está bienequals()
.hashCode()
debe usar el subconjunto de propiedades que no cambiaequals()
fuente
Saving an object will change it's state
!hashCode
debe volverint
, entonces, ¿cómo va a usargetName()
? ¿Puede dar un ejemplo para suhashCode
Una aclaración sobre el
obj.getClass() != getClass()
.Esta declaración es el resultado de
equals()
ser una herencia hostil. Los JLS (especificación del lenguaje Java) especifica que siA.equals(B) == true
entoncesB.equals(A)
también hay que volvertrue
. Si omite esa declaración, las clases heredadas que anulanequals()
(y cambian su comportamiento) romperán esta especificación.Considere el siguiente ejemplo de lo que sucede cuando se omite la declaración:
Haciendo
new A(1).equals(new A(1))
también, elnew B(1,1).equals(new B(1,1))
resultado es verdadero, como debería ser.Todo esto se ve muy bien, pero mira lo que sucede si intentamos usar ambas clases:
Obviamente, esto está mal.
Si desea garantizar la condición simétrica. a = b si b = a y el principio de sustitución de Liskov llama
super.equals(other)
no solo en el caso de laB
instancia, sino que comprueba después, porA
ejemplo:Lo que dará salida:
Donde, si
a
no es una referencia deB
, entonces podría ser un ser un referente de claseA
(porque extenderla), en este caso se llamasuper.equals()
demasiado .fuente
ThingWithOptionSetA
puede ser igual a a,Thing
siempre que todas las opciones adicionales tengan valores predeterminados, y del mismo modo para aThingWithOptionSetB
, entonces debería ser posibleThingWithOptionSetA
comparar a igual a aThingWithOptionSetB
solo si todas las propiedades no básicas de ambos objetos coinciden con sus valores predeterminados, pero No veo cómo se prueba para eso.B b2 = new B(1,99)
, entoncesb.equals(a) == true
ya.equals(b2) == true
perob.equals(b2) == false
.Para una implementación amigable con la herencia, consulte la solución de Tal Cohen, ¿Cómo implemento correctamente el método equals ()?
Resumen:
En su libro Guía efectiva del lenguaje de programación Java (Addison-Wesley, 2001), Joshua Bloch afirma que "simplemente no hay forma de extender una clase instanciable y agregar un aspecto mientras se preserva el contrato igual". Tal no está de acuerdo.
Su solución es implementar equals () llamando a otro no simétrico blindlyEquals () en ambos sentidos. blindlyEquals () se reemplaza por subclases, se hereda equals () y nunca se reemplaza.
Ejemplo:
Tenga en cuenta que equals () debe funcionar en todas las jerarquías de herencia si se debe cumplir el Principio de sustitución de Liskov .
fuente
if (this.getClass() != o.getClass()) return false
, pero flexible, ya que solo devuelve falso si las clases derivadas se molestan en modificar iguales. ¿Está bien?Todavía sorprendido de que ninguno recomendó la biblioteca de guayaba para esto.
fuente
this
enthis.getDate()
medios nada (que no sea el desorden)if (!(otherObject instanceof DateAndPattern)) {
. De acuerdo con Hernan y Steve Kuo (aunque ese es un asunto de preferencia personal), pero de todos modos +1.Hay dos métodos en superclase como java.lang.Object. Necesitamos anularlos al objeto personalizado.
Los objetos iguales deben producir el mismo código hash siempre que sean iguales, sin embargo, los objetos desiguales no necesitan producir códigos hash distintos.
Si desea obtener más, consulte este enlace como http://www.javaranch.com/journal/2002/10/equalhash.html
Este es otro ejemplo, http://java67.blogspot.com/2013/04/example-of-overriding-equals-hashcode-compareTo-java-method.html
¡Que te diviertas! @. @
fuente
Hay un par de formas de verificar la igualdad de clase antes de verificar la igualdad de los miembros, y creo que ambas son útiles en las circunstancias correctas.
instanceof
operador.this.getClass().equals(that.getClass())
.Uso # 1 en una
final
implementación igual, o cuando implemento una interfaz que prescribe un algoritmo para iguales (como lasjava.util
interfaces de colección, la forma correcta de verificar con(obj instanceof Set)
o con cualquier interfaz que esté implementando). En general, es una mala elección cuando se pueden anular iguales porque eso rompe la propiedad de simetría.La opción # 2 permite que la clase se extienda de manera segura sin anular iguales o romper la simetría.
Si su clase también lo es
Comparable
, los métodosequals
y tambiéncompareTo
deben ser consistentes. Aquí hay una plantilla para el método igual en unaComparable
clase:fuente
final
, y elcompareTo()
método se anuló para invertir el orden de clasificación, las instancias de la subclase y la superclase no deberían considerarse iguales. Cuando estos objetos se usaron juntos en un árbol, las claves que eran "iguales" según unainstanceof
implementación podrían no ser encontrables.Para los iguales, busque Secretos de los iguales de Angelika Langer . Yo la amo mucho. También es una gran pregunta frecuente sobre genéricos en Java . Vea sus otros artículos aquí (desplácese hacia abajo hasta "Core Java"), donde también continúa con la Parte 2 y la "comparación de tipos mixtos". ¡Diviértete leyéndolos!
fuente
El método equals () se utiliza para determinar la igualdad de dos objetos.
ya que el valor int de 10 siempre es igual a 10. Pero este método equals () trata sobre la igualdad de dos objetos. Cuando decimos objeto, tendrá propiedades. Para decidir sobre la igualdad, se consideran esas propiedades. No es necesario que se tengan en cuenta todas las propiedades para determinar la igualdad y, con respecto a la definición de clase y el contexto, se puede decidir. Entonces el método equals () puede ser anulado.
siempre debemos anular el método hashCode () siempre que anulemos el método equals (). Si no, ¿qué pasará? Si usamos tablas hash en nuestra aplicación, no se comportará como se esperaba. Como el hashCode se usa para determinar la igualdad de los valores almacenados, no devolverá el valor correspondiente correcto para una clave.
La implementación predeterminada dada es el método hashCode () en la clase Object utiliza la dirección interna del objeto y lo convierte en entero y lo devuelve.
Ejemplo de salida de código:
fuente
Lógicamente tenemos:
a.getClass().equals(b.getClass()) && a.equals(b)
⇒a.hashCode() == b.hashCode()
¡Pero no al revés!
fuente
Un problema que he encontrado es donde dos objetos contienen referencias entre sí (un ejemplo es una relación padre / hijo con un método de conveniencia en el padre para obtener todos los hijos).
Este tipo de cosas son bastante comunes cuando se hacen asignaciones de Hibernate, por ejemplo.
Si incluye ambos extremos de la relación en su código hash o pruebas iguales, es posible entrar en un bucle recursivo que termina en una excepción StackOverflowException.
La solución más simple es no incluir la colección getChildren en los métodos.
fuente
equals()
. Si un científico loco creara un duplicado de mí, seríamos equivalentes. Pero no tendríamos el mismo padre.