How to override equals and hashCode method in Java and Hibernate - Example, Tips and Best Practices
Override equals and hashCode in Java
Equals and hashCode in Java are two fundamental method which is declared in Object class and part or core Java library. equals() method is used to compare Objects for equality while hashCode is used to generate an integer code corresponding to that object. equals and hashCode has used extensively in Java core library like they are used while inserting and retrieving Object in HashMap, see how HashMap works in Java for full story, equals method is also used to avoid duplicates on HashSet and other Set implementation and every other place where you need to compare Objects. Default implementation of equals() class provided byjava.lang.Object compares memory location and only return true if two reference variable are pointing to same memory location i.e. essentially they are same object. Java recommends to override equals and hashCodemethod if equality is going to be define by logical way or via some business logic and many classes in Java standard library does override it e.g. String overrides equals, whose implementation of equals() method return true if content of two String objects are exactly same. Integer wrapper class overrides equals to perform numerical comparison etc.
Since HashMap and Hashtable in Java relies on equals() and hashCode() method for comparing keys and values, Java provides following rules to override equals method Java. As per following rule equals method in Java should be:
1) Reflexive : Object must be equal to itself.
2) Symmetric : if a.equals(b) is true then b.equals(a) must be true.
3) Transitive : if a.equals(b) is true and b.equals(c) is true then c.equals(a) must be true.
4) Consistent : multiple invocation of equals() method must result same value until any of properties are modified. So if two objects are equals in Java they will remain equals until any of there property is modified.
5) Null comparison : comparing any object to null must be false and should not result inNullPointerException. For example a.equals(null) must be false, passing unknown object, which could be null, to equals in Java is is actually a Java coding best practice to avoid NullPointerException in Java.
Equals and hashCode contract in Java
And equals method in Java must follow its contract with hashcode method in Java as stated below.
1) If two objects are equal by equals() method then there hashcode must be same.
2) If two objects are not equal by equals() method then there hashcode could be same or different.
So this was the basic theory about equals method in Java now we are going to discuss the approach on how to override equals() method, yes I know you all know this stuff :) but I have seen some of equals() code which can be improved by following correct approach. For illustration purpose we will see an example of Person class and discuss How to write equals() method in Java for that class.
Steps to Override equals method in Java
Here is my approach for overriding equals method in Java. This is based on standard approach most of Java programmer follows while writing equals method in Java.
2) Do null check -- if yes then return false.
3) Do the instanceof check, if instanceof return false than return false from equals in Java , after some research I found that instead of instanceof we can use getClass() method for type identification becauseinstanceof check returns true for subclass also, so its not strictly equals comparison until required by business logic. But instanceof check is fine if your class is immutable and no one is going to sub class it. For example we can replace instanceof check by below code
if((obj == null) || (obj.getClass() != this.getClass()))
return false;
return false;
4) Type cast the object; note the sequence instanceof check must be prior to casting object.
5) Compare individual attribute starting with numeric attribute because comparing numeric attribute is fast and use short circuit operator for combining checks. If first field does not match, don't try to match rest of attribute and return false. It’s also worth to remember doing null check on individual attribute before calling equals() method on them recursively to avoid NullPointerException during equals check in Java.
Code Example of overriding equals method in Java
Let’s see a code example based on my approach of overriding equals method in Java as discussed in above paragraph and hashCode() method is generated by Eclipse IDE, see my post 5 tips to override hashcode in Java for detailed example and explanation of overriding hashcode.
/**
* Person class with equals and hashcode implementation in Java
* @author Javin Paul
*/
public class Person {
private int id;
private String firstName;
private String lastName;
public int getId() { return id; }
public void setId(int id) { this.id = id;}
public String getFirstName() { return firstName; }
public void setFirstName(String firstName) { this.firstName = firstName; }
public String getLastName() { return lastName; }
public void setLastName(String lastName) { this.lastName = lastName; }
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null || obj.getClass() != this.getClass()) {
return false;
}
Person guest = (Person) obj;
return id == guest.id
&& (firstName == guest.firstName
|| (firstName != null &&firstName.equals(guest.getFirstName())))
&& (lastName == guest.lastName
|| (lastName != null && lastName .equals(guest.getLastName())));
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((firstName == null) ? 0 : firstName.hashCode());
result = prime * result + id;
result = prime * result
+ ((lastName == null) ? 0 : lastName.hashCode());
return result;
}
}
* Person class with equals and hashcode implementation in Java
* @author Javin Paul
*/
public class Person {
private int id;
private String firstName;
private String lastName;
public int getId() { return id; }
public void setId(int id) { this.id = id;}
public String getFirstName() { return firstName; }
public void setFirstName(String firstName) { this.firstName = firstName; }
public String getLastName() { return lastName; }
public void setLastName(String lastName) { this.lastName = lastName; }
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null || obj.getClass() != this.getClass()) {
return false;
}
Person guest = (Person) obj;
return id == guest.id
&& (firstName == guest.firstName
|| (firstName != null &&firstName.equals(guest.getFirstName())))
&& (lastName == guest.lastName
|| (lastName != null && lastName .equals(guest.getLastName())));
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((firstName == null) ? 0 : firstName.hashCode());
result = prime * result + id;
result = prime * result
+ ((lastName == null) ? 0 : lastName.hashCode());
return result;
}
}
If you look above method we are first checking for "this" check which is fastest available check for equals method then we are verifying whether object is null or not and object is of same type or not. only after verifying type of object we are casting it into desired object to avoid any ClassCastException in Java. Also while comparing individual attribute we are comparing numeric attribute first using short circuit operator to avoid further calculation if its already unequal and doing null check on member attribute to avoid NullPointerException.
Common Errors while overriding equals in Java
Though equals() and hashcode() method are defined in Object class along with wait, notify and notifyAll, and one of fundamental part of Java programming I have seen many programmers making mistake while writingequals() method in Java. I recommend all Java programmer who has just started programming to write couple ofequals and hashcode method for there domain or value object to get feel of it. Here I am listing some of common mistakes I have observed on various equals method in Java, if you like to learn more about common mistakes in Java programming then see my post Don’t use float and double for monetary calculation and Mixing static and non static synchronized method. Now let’s see common mistakes by Java programmers while overriding equals in Java :
1) Instead of overriding equals() method programmer overloaded it.
This is the most common error I have seen while overriding equals method in Java. Syntax of equals method defined in Object class is public boolean equals(Object obj) but many people unintentionally overloads equals method in Java by writing public boolean equals(Person obj), instead of using Object as argument they use there class name. This error is very hard to detect because of static binding. So if you call this method in your class object it will not only compile but also execute correctly but if you try to put your object in collection e.g. ArrayList and call contains() method which is based on equals() method in Java it will not able to detect your object. So beware of it. This question is also a frequently asked question in Java interviews as part of Overloading vs Overriding in Java as how do you prevent this from happening ? Thankfully along-with Generics, Enum, autoboxing and varargs Java 5 also introduces @Override annotation which can be used to tell compiler that you are overriding a method and than compiler will be able to detect this error during compile time. Consistently using @Override annotation is also a best practice in Java.
2) Second mistake I have seen while overriding equals() method is not doing null check for member variables which ultimately results in NullPointerException in Java during equals() invocation. For example in above code correct way of calling equals() method of member variable is after doing null check as shown below:
firstname == guest.firstname || (firstname != null &&firstname.equals(guest.firstname)));
3) Third common mistake is not overriding hashCode method in Java and only overriding equals() method. You must have to override both equals() and hashCode() method in Java , otherwise your value object will not be able to use as key object in HashMap because working of HashMap is based on equals() and hashCode to read more see , How HashMap works in Java.
4) Last common mistake programmer make while overriding equals() in Java is not keeping equals() andcompareTo() method consistent which is a non formal requirement in order to obey contract of Set to avoid duplicates. SortedSet implementation like TreeSet uses compareTo to compare two objects like String and ifcompareTo() and equals() will not be consistent than TreeSet will allow duplicates which will break Set contract of not having duplicates. To learn more about this issue see my post Things to remember while overridingcompareTo in Java
Writing JUnit tests for equals method in Java
Its good coding practice to write JUnit test cases to test your equals and hashCode method. Here is my approach for writing JUnit test case for equals method in Java. I will write test cases to check equals behaviour, contract of equals and hasCode method and properties of equals method in Java on different circumstances. You can alsoJUnit4 annotation to write JUnit test cases, than you don’t need to use test prefix on test method, just use @Test annotations.
testReflexive() this method will test reflexive nature of equals() method in Java.
testSymmeteric() this method will verify symmetric nature of equals() in Java.
testNull() this method will verify null comparison and will pass if equals method returns false.
testConsistent() should verify consistent nature of equals method in Java.
testNotEquals() should verify if two object which are not supposed to equals is actually not equal, having negative test cases in test suite is mandatory.
testHashCode() will verify that if two objects are equal by equals() method in Java then there hashcode must be same. This is an important test if you are thinking to use this object as key in HashMap or Hashtable
5 Tips on writing equals method in Java
Here are some tips to implement equals and hashCode method in Java, this will help you to do it correctly and with ease:
1) Most of the IDE like NetBeans, Eclipse and IntelliJ IDEA provides support to generate equals() andhashcode() method. In Eclipse do the right click-> source -> generate hashCode() and equals().
2) If your domain class has any unique business key then just comparing that field in equals method would be enough instead of comparing all the fields e.g. in case of our example if "id" is unique for every Person and by just comparing id we can identify whether two Person are equal or not.
3) While overriding hashCode in Java makes sure you use all fields which have been used in equals method in Java.
4) String and Wrapper classes like Integer, Float and Double override equals method but StringBufferdoesn’t override it.
5) Whenever possible try to make your fields immutable by using final variables in Java, equals method based on immutable fields are much secure than on mutable fields.
6) Don't use instanceof check in equals method, as it could break contract of equals() method in sub-class, results in non-symmetric equals, because instanceof return true for child class as well. For example, if you compare two objects Parent and Child from same type-hierarchy; Parent.equals(child) will returntrue, because child instanceof Parent is true, but Child.equals(parent) will return false, because Parent instanceof Child is false. This means equals() is not following symmetric contract, which states if a.equals(b) == true than b.equals(a) == true, as shown below :
public class Parent {
}
public class Child extends Parent {
}
public class InstanceOfCheck {
public static void main(String args[]) {
Parent p = new Parent();
Child c = new Child();
System.out.println("child instanceof Parent " + (c instanceof Parent));
System.out.println("parent instanceof Child " + (p instanceof Child));
}
}
Output :
child instanceof Parent true
parent instanceof Child false
Alternatively, you can mark equals as final method to prevent it from being overridden.
7) While comparing String object in Java, prefer equals() than == operator.
8) Use IDE or Apache commons EqualsBuilder and HashCodeBuilder utility classes to automatically implement equals() and hashcode() method in Java.
9) Two object which is logically equal but loaded from different ClassLoader cannot be equals. Remember that getClass() check, it will return false if class loader is different.
10) Use @Override annotation on hashcode() as well, as it also prevents subtle mistakes over return type. e.g. return type of hashcode() method is int, but many times programmers mistakenly put long.
11) One example, where equals method is not consistent with compareTo is java.math.BigDecimal class. If you compare two BigDecimal object e.g. 120.00 and 120.000, equals method will return false, while compareTo will return zero. Both are inconsistent, because equals take both scale and value in consideration, while compareTo method only consider values.
Use of Equals and Hashcode Method in Hibernate
Hibernate is a popular, open source Java persistent framework, which provides Object Relational Mapping, also known as ORM framework. It uses equals and hashcode method to provide object's equality in Java side. You should override equals() and hashcode() if :
1) You are storing instance of persistent class in a Set for representing many-valued associations.
2) You are using reattachment of detached persistent instances.
Another worth noting point is that Hibernate only guarantees equivalence of database row (persistent identity) and Java object inside a particular Session. Which means if you store instances retrieved in different Sessions in a Set, you will be having duplicates. Now the most important aspect of overriding equals andhashcode() for hibernate entity classes, you should never decide equality just based upon identifier. Though it’s convenient to compare identifier to see if the belong to same database row, Unfortunately, we can't use this approach with generated identifiers. Since Hibernate only assign identifier values to the object that are persistent, a newly created instance will not have any identifier value. Similarly, if an instance is not persisted, and currently in a Set, saving it to database will assigned an identifier value, which will further change the value of hashcode() method, finally results in breaking the contract of the Set. That's why it's best to implement equals and hashcode in Hibernate using business key equality e.g. an Employee is same if it's name, surname, father's name, department, date of birth is same. Properties which are not prone to change e.g. date of birth are better candidate of business equality than those which is easier to change e.g. address and contact number.
In short, remember these best practices while overriding equals() and hashcode() for Hibernate entity class :
1) Don't let your equals() method only uses identifier values for equivalence check.
2) Implement equals() and hashCode() using real word key that would identify instance in real world.
3) Use Immutable and unique properties of objects for equality.
That’s all about overriding equals() and hashcode() methods in Java, I am reiterating this but its imperative for a Java programmer to be able to write equals , hashcode(), compareTo() method by hand. It is not just useful for learning purpose but to clear any coding exercise during Java interviews. Writing code for equals and hashcode is very popular programming interview questions now days.
1 comment:
Best article. Thanks for sharing.
https://www.flowerbrackets.com/difference-between-equal-operator-vs-equals-method-java/
Post a Comment