Regarding adding equals and hashCode for JPA/Hibernate entities, please see this StackOverflow Answer.
The code pattern recommended below depends upon the new instanceof pattern matching functionality released for preview in Java 14 (2020/Mar/17), and then fully released in Java 16 (2021/Mar/16).
I really dislike seeing explicit uses of true and false, especially when they fall directly out of evaluated logic.
So, I am not at all fond of the default code pattern generated by my IDE (IntelliJ) when overriding and implementing equals() in my classes.
It looks like this:
//Legacy laden pattern produced by IntelliJ code generator
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
EqualsExampleClass that = (EqualsExampleClass) o;
return Objects.equals(stringy, that.stringy)
&& (inty == that.inty)
&& Objects.equals(bigIntegery, that.bigIntegery)
&& (booleany == that.booleany);
}
Encouraged by Oracle JEP 394 for the instanceof pattern matching, I've refactored the above code to look like this:
//Modern flowing fluent style
@Override
public boolean equals(Object o) {
return (this == o) ||
((o instanceof EqualsExampleClass that)
&& (this.getClass() == that.getClass())
&& Objects.equals(this.stringy, that.stringy)
&& (this.inty == that.inty)
&& Objects.equals(this.bigInt, that.bigInt)
&& (this.booleany == that.booleany));
}
Per line execution details:
(this == o) - checks for literally the same instance
(o instanceof EqualsExampleClass that) - checks to ensure o is not null, o is an instance of this class (or a descendant), and if so, assigns o to the narrowed type variable that
(this.getClass() == that.getClass()) - requires the equals comparison is between two instances of the same class (i.e., that is not an instance of a descendant class), and can be removed if, and only if, this class is marked final
Objects.equals(this.stringy, that.stringy) - is using Objects.equals() on references (not primitives) to ensure that if either side returns a null, the comparison is successfully made without throwing a NullPointerException
This code implementation pattern is considerably easier to functional flow read, analyze, reason about, and debug, during a refactoring pass.