As I have mention in my previous post that hashCode() and eqauls() implementation are basic concepts of Java and any developer should know about these while using Collection classes. These are important concepts and even asked in technical interviews. When equals() method is implemented it is also necessary to implement hashCode() method. In this post, implementation details of equals() method have been discussed. hashCode() method implementation contract can be found here.

equals() method contract

This method implements equivalence relation on non-null object reference. All the requirement should be followed otherwise Collection classes will not work properly.

  • It should be reflexive: For non-null reference x, x.equals(x) should return true.
  • It should be symmetric: For non-null reference x, y. If x.equals(y) returns true then y.equals(x) should also return true.
  • It should be transitive: For non-null reference x, y and z, If x.equals(y) and y.equals(z) return true then x.equals(z) should return true.
  • It should return consistent result during multiple invocations if the fields’ value are not change during the invocations
  • x.equals(null) should return false if x is non-null reference.

Algorithm for implementation

There is a simple algorithm one should follow while writing equals() method. This algorithm is not stringent and can be modified, but implementation should meet all the requirements mentioned above.

  1. use “==” to check reference equality. If references are same then return true
  2. Make null check. If the given object is null then return false. This is very important, If this one is ignored then you will get NullPointerException when null is passed an object.
  3. Use getClass()/instanceof to make sure that right objects are compared. Returns false if object kind doesn’t match.
  4. Cast the object to the current class object type
  5. Individually test each field/member variables. If those are class object, call their equals() method. If this equals() method returns false then immediately return false. Otherwise move to other field/member variable.
  6. If every test mention above is positive then return true
Note:
Always compare constant object with the provided object e.g
"Code4Reference".equals(otherObject) This is more stable than otherObject.equals("Code4Reference") because former returns false in case otherObject is null, but later thorows NullPointerException.

Some implementations use instanceof operator to check objects. This works fine if you don't want to override equals() method in child class and want to use the parent class implementation. This approach may not work well if child class has more fields. In such case hashCode() and equals() should be implemented in child classes and getClass() method should be used. If getClass() method is used then equals() method usage get restricted to the same class. It fails to compare derived class object. Choose one of these according to the requirements. Java has used instanceof operator in most of the implementations.

Some of the examples where above algorithm is not followed but equivalence requirements are met.

 public boolean equals(Object obj) {
    if (obj instanceof Integer) {
        return value == ((Integer)obj).intValue();
    }
    return false;
 }
public boolean equals(Object obj) {
    return obj instanceof Date && getTime() == ((Date) obj).getTime();
}

Here is the sample equals() method implementation.

public class Employee {
	private int employeeId;
	private String name;
	private String title;
	private Department dept;
	...
	...
	...
@Override
	public boolean equals(Object obj) {
		if (this == obj)  //Reference check, if it is same object return true.
			return true;
		if (obj == null)  //Return false if given object is null.
			return false;
		if (getClass() != obj.getClass()) //Make sure give object is of right type.
			return false;
		Employee other = (Employee) obj;  //convert object into current class object.
                //Now compare all member variables.
		if (dept == null) {
			if (other.dept != null)
				return false;
		} else if (!dept.equals(other.dept))
			return false;
		if (employeeId != other.employeeId) {
			return false;
                }
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name)) {
			return false;
                }
		if (title == null) {
			if (other.title != null)
				return false;
		} else if (!title.equals(other.title)) {
			return false;
                }
               //If every check is positive then return true.
		return true;
	}

	...
	...
	...
}

If you are using Eclipse IDE for code development then its really easy to generate equals() implementation by few button clicks. In Eclipse Source > Generate hashCode() and equals(). voila!
Eclipse-hashCode-equals-implementation.png

Common pitfalls

Well not to mention, these requirements and algorithm looks simple but often developer misses the nuances and they land-up in debugging their inappropriate code. These are the common pitfalls and should be avoided.

  • Wrong method signature, If the method signature is different than public boolean equals(Object ob) then it is not function overriding but function overloading. Developer miss this nuance and waste hours in debugging the issue.
  • Using different field used for hasCode() calculation: sometimes developer change equal() method implementation but forget to change hashCode() method. Rule of thumb, whenever you change hashcode() method you should change other equal() method too and vice-versa.
  • When fields are not immutable, you get weird problems. For example, If object is put in HashMap and later the one of the object fields is changed. When you try to find the object in HashMap it wouldn't return the object because the hashCode is changed and HashMap still has the old one which doesn't match.
  • Failing to define equal as an equivalence method. When not all of the requirements are followed and class objects are used with Collection classes we get erroneous results.
  • Not using @Override annotation. This annotation saves your lots of time. It throws compilation error if the function signature doesn't match and you get to know instantly.



Hope this blog helped you in some way. If you like this blog then please share it. You can also leave your comment below. You can find Facebook page here.

Related topics

  1. Difference between "==" and equals() method in Java
  2. Rules for hashCode() implementation
, ,
Trackback

no comment untill now

Add your comment now