Tengo este @Override for equals() en mi clase MyClass :
@Entity( name = "MyClass" ) @Table( name = "my_class" ) public class MyClass extends MySuperClass { ... @Override public boolean equals( Object o ) { if ( this == o ) { return true; } if ( o == null || this.getClass() != o.getClass() ) { return false; } if ( !super.equals( o ) ) { return false; } MyClass that = ( MyClass ) o; return this.var1.equals( that.var1 ) && this.var2.equals( that.var2 ); } ... } Bastante estándar. De hecho, sigue las mejores prácticas de Java.
Más tarde en la vida tengo esto en otra clase de subpaquete (mi clase de controlador):
... package com.abapi.controllers; ... import com.abjpa.models.MyClass; ... MyClass myObject1 = new MyClass( var1, var2 ); MyClass myObject2 = this.myClassRepository.getById( 1 ); // SpringBoot/Jpa/Hibernate if ( myObject2.equals( myObject1 ) ) { ...do something... } ... this.myClassRepository.save( myObject1 ); ...Mi problema es que .equals() siempre falla aquí:
if ( o == null || this.getClass() != o.getClass() )porque java dice que this.getClass() y o.getClass() NO son iguales. Cuando depuro el código (en Intellij IDEA 2022.1 UE) veo esto:
this.getClass() = MiClase@13706
pero
o.getClass = com.abjpa.models.MyClass@8f7462
¡Pero son de la misma clase! Casi todos los libros, tutoriales, blogs, Intellij IDEA, etc. de Java demuestran el .equals() de esta manera. Probé esto en Ubuntu 20.04.4 LTS java-14-openjdk-amd64 y java-17-openjdk-amd64 con los mismos resultados.
¿Qué estoy haciendo mal?
myObject2 es una instancia de una clase proxy, generada en tiempo de ejecución por Hibernate usando Byte Buddy . El proxy generado intercepta todas las invocaciones de métodos, por eso getClass() devuelve resultados diferentes.
Como alternativa a getClass() , el uso de instanceof podría ser otro enfoque:
if ( !(this instanceof MyClass && o instanceof MyClass) ) { return false; } Sin embargo, tenga en cuenta que instanceof tiene sus inconvenientes . Viola el principio de simetría.
En primer lugar, no debe comparar estos objetos, ya que un objeto nuevo debe ser diferente de uno administrado por Hibernate que tiene un estado persistente.
Utilizo estas implementaciones estándar de hashcode y equals para entidades JPA (como se describe aquí ):
@Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof MyClass)) { return false; } MyClass other = (MyClass) o; return id != null && id.equals(other.getId()); } @Override public int hashCode() { return getClass().hashCode(); }No está utilizando una mejor práctica. Cambie sus iguales para que sean algo como lo siguiente (verificación nula primero, luego la misma verificación, luego verificación de clase):
public boolean equals(Object obj) { if (obj == null) { return false; } if (obj == this) { return true; } if (obj.getClass() != getClass()) { return false; } ... do the actual comparison here }Nota: el código anterior es una paráfrasis de Apache EqualsBuilder