Why Should we override equals method in Java

In this tutorial,we will try to understand need for overriding equals method in Java.



Let's start this topic with something very basic i.e. object. What is Object?

Anything which has state and behavior can be treated as object in Object oriented language.

State - represented by instance variables of class
behavior - represented by methods of class

For example every one of us has some state and behavior.

Our state can be represented by
- name
- address
- fatherName
- motherName

and behavior can be represented by
- eat()
- sleep()
- work()

Now if have to give a name to the Class(group) to which these objects belong, we can name it HumanBeing and class will look like as below :
package nl.blogspot.javasolutionsguide;

class HumanBeing {
    String name;
    String address;
    String fatherName;
    String motherName;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getFatherName() {
        return fatherName;
    }

    public void setFatherName(String fatherName) {
        this.fatherName = fatherName;
    }

    public String getMotherName() {
        return motherName;
    }

    public void setMotherName(String motherName) {
        this.motherName = motherName;
    }

    public void eat() {
        System.out.println("Eat  like normal human beings");
    }

    public void sleep() {
        System.out.println("Sleep  like normal human beings");
    }

    public void work() {
        System.out.println("Work like normal human beings");
    }
}

Now this is general class representing all Human beings, but there are some special type of creatures in this world, who work day and night on computer to do coding and people call them Software developers, but they are human beings only, so let us create a class SoftwareDeveloper by extending HumanBeing class as below :
package nl.blogspot.javasolutionsguide;

class SoftwareDeveloper extends HumanBeing {

    String unit;
    String companyName;
    String technology;
    String totalExperience;

    public String getTotalExperience() {
        return totalExperience;
    }

    public void setTotalExperience(String totalExperience) {
        this.totalExperience = totalExperience;
    }

    public String getCompanyName() {
        return companyName;
    }

    public void setCompanyName(String companyName) {
        this.companyName = companyName;
    }

    public String getTechnology() {
        return technology;
    }

    public void setTechnology(String technology) {
        this.technology = technology;
    }

    public String getUnit() {
        return unit;
    }

    public void setUnit(String unit) {
        this.unit = unit;
    }

    public void work() {
        System.out.println("Keep Working");
    }

    public void eat() {
        System.out.println("Eat occasionally");
    }

    public void sleep() {
        System.out.println("Sleep occasionally");
    }
}


Let us now create a test Class where we will create objects of SoftwareDeveloper class and compare them.
package nl.blogspot.javasolutionsguide;

public class TestClass {

    public static void main(String[] args) {
        SoftwareDeveloper softwareDeveloper1 = new SoftwareDeveloper();
        softwareDeveloper1.setName("Gaurav");
        softwareDeveloper1.setCompanyName("ABC Limited");
        softwareDeveloper1.setTechnology("Java");
        softwareDeveloper1.setTotalExperience("8");
        softwareDeveloper1.setUnit("Fiancial Services");

        SoftwareDeveloper softwareDeveloper2 = new SoftwareDeveloper();
        softwareDeveloper2.setName("Gaurav");
        softwareDeveloper2.setCompanyName("ABC Limited");
        softwareDeveloper2.setTechnology("Java");
        softwareDeveloper2.setTotalExperience("8");
        softwareDeveloper2.setUnit("Fiancial Services");

        /*Below comparison is like saying that two physically existing
          individuals(developer) are one and same or equal, which can never
          be the case we know.*/
        if (softwareDeveloper1 == softwareDeveloper2) {
            System.out.println("Both are equally good");
        } else {
            System.out.println("Both are not equally good");
        }

    }
}
  
Output :
Both are not equally good
In the TestClass, we are creating two objects of SoftwareDeveloper class and both of them has exactly same state(properties),but still they are two different individuals, so we can not say that they are one and same.

TakeAway 

When we compare objects using ==  it compares if they are same objects in memory and not the values (states) in the object.

What if we use equals in above TestClass to compare two SoftwareDeveloper instances
package com.test.equals;

public class TestClass {

    public static void main(String[] args) {
        SoftwareDeveloper softwareDeveloper1 = new SoftwareDeveloper();
        softwareDeveloper1.setName("Gaurav");
        softwareDeveloper1.setCompanyName("ABC Limited");
        softwareDeveloper1.setTechnology("Java");
        softwareDeveloper1.setTotalExperience("8");
        softwareDeveloper1.setUnit("Fiancial Services");

        SoftwareDeveloper softwareDeveloper2 = new SoftwareDeveloper();
        softwareDeveloper2.setName("Gaurav");
        softwareDeveloper2.setCompanyName("ABC Limited");
        softwareDeveloper2.setTechnology("Java");
        softwareDeveloper2.setTotalExperience("8");
        softwareDeveloper2.setUnit("Fiancial Services");

        if (softwareDeveloper1.equals(softwareDeveloper2)) {
            System.out.println("Both are equally good");
        } else {
            System.out.println("Both are not equally good");
        }
    }
}
Output :
Both are not equally good
As we can see still, these two software developers are not being considered equal. Reason being when we are calling equals() method on SoftwareDeveloper's instance, it is actually calling equals() method in the Object class, which is parent of all java classes and by default all public methods of Object class are available to all the Java classes. Below is implementation of equals() method in Object class:

public boolean equals(Object obj) {
      return (this == obj);
}

As we can see here also, it is using == ,hence same result.

Take Away 

If we do not override equals() method in our classes, the default implementation from Object class will be available to all the classes and if we will try to compare objects using equals(),it will compare using == and will consider two objects equal only when they are referring to same object in memory.

Now one may ask, are above two approaches to compare objects is wrong?

Answer is Yes as well as No.

No, because it depends upon our requirements. If our requirements is such that we do not want to consider two different objects in memory as same even when they have same values, then there is nothing wrong with above approaches.

Yes, because there might be requirements in the application in which above code is being used that if two SoftwareDevelopers have same experience ,then consider them as equal. For example for HR department, they may give same salary to two developers with same experience ,so for them two developers with same experience are equal, although still they are two different individuals(different objects in memory).

Now one may ask, how we can make HR module of application understand that two different objects in memory are actually equal.

Answer is: we need to override equals method such that two objects should be considered equal when the value of property(instance variables) totalExperience is same.

Let us override equals() method in  SoftwareDeveloper class, such that we consider two objects equal when they have same totalExperience.  
@Override
public boolean equals(Object obj){
        if(this==obj){
            return true;
        }
        if(obj==null){
            return false;
        }
        if(getClass()!=obj.getClass()){
            return false;
        }
        SoftwareDeveloper other=(SoftwareDeveloper)obj;
        if(totalExperience==null){
            if(other.totalExperience!=null)
                return false;
            }else if(!totalExperience.equals(other.totalExperience)){
                return false;
            }
            return true;
        }
}

How above equals method is getting called 

if(softwareDeveloper1.equals(softwareDeveloper2)) {
 System.out.println("Both are equally good");
} else{
 System.out.println("Both are not equally good");
}

Let us try to understand above equals method code:
if (this == obj){
    return true;
}

Here this refers to instance softwareDeveloper1 and obj refers to softwareDeveloper2 and we are comparing them with == .We are doing this first ,because if two objects are same objects in memory, obviously they are equal and there is no need to check for their values and equals method can return true in that case any ways.

if (obj == null){
   return false;
}


if we have reached inside equals method ,that means softwareDeveloper1 is definitely not null as we have invoked equals method on softwareDeveloper1 instance, but it is possible that softwareDeveloper2 will be null, in which they will not be == and hence equals should return false.
if (getClass() != obj.getClass()){
    return false;
}
getClass returns the runtime class of the object on which it is called. So here we are checking ,if softwareDeveloper1 and softwareDeveloper2 both belong to same class at runtime, if they are not belonging to same runtime class, obviously they are not equal, so equals return false.

Note : if you will print output of getClass() and obj.getClass(),you will get below :
class com.test.equals.SoftwareDeveloper
class com.test.equals.SoftwareDeveloper
        SoftwareDeveloper other = (SoftwareDeveloper) obj;
        if (totalExperience == null) {
            if (other.totalExperience != null)
                return false;
            } else if (!totalExperience.equals(other.totalExperience)){
                return false;
            }
            return true;
        }
 
In above code, first we are checking whether totalExperience instance variable value of softwareDeveloper1 is null and of softwareDeveloper2 is not null. If this is the case then as totalExperience value is not equal, equals method will return false.

Next we check,if softwareDeveloper1's totalExperience is not equal to softwareDeveloper2's totalExperience ,then also equals method returns false.

And if we have crossed passed all above if, elses and equals method have not returned yet, that means totalExperience of softwareDeveloper1 and softwareDeveloper2 are equal.

Now ,after overriding equals method, if we run our TestClass code, we will get following output :

Both are equally good

Take Away :

Depending upon our functional requirements, we would like to consider two different objects in memory as equal by overriding equals method where in we can check for equality of the property(ies) of two instances and can decide whether they should be considered equal or not.

In the next tutorial we will see why we should override hashCode() method when we override equals().

Thanks for reading!!!