equals() method of Object class in Java

When you compare two object references using == operator, it evaluate to true only when both references refer to same object.
public class Person {
 private Integer id;
 private String name;
 public Person(Integer id, String name) {
  this.id = id;
  this.name = name;
 }
 
 public static void main(String[] args) {
  Person p1 = new Person(1, "Prithvi");
  Person p2 = p1;
  Person p3= new Person(1, "Prithvi");
  System.out.println(p1==p2);
  System.out.println(p1==p3);
 }
 
}

Output:
true
false

We have created 3 references of Person class. Object references p1 and p2 both refer to same object so comparison evaluate to true. Comparison of p1 and p3 evaluate to false, even though p3 has same contents as p1 but refer to different object so comparison returns false. Below image explains this -




Objects p3 and p1 are meaningfully equal. We must have some way to see their content are same or not. But the question is, How will you compare two different objects to see their contents are meaningfully equal?
You can use equals() method for content comparison. But equals() method in Object class uses == operator to check quality. Please see below code snippet of equals() method from Object class.

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

You need to override equals() method in your class to make it work.

Rules:

You need to consider below rules while overriding equals() method-
1. If you pass null in equals() method, it should not throw NullPointerException.
2. If you pass object of other class, it should not throw ClassCastException
Note:If null and object of class is passed in equals method, it must return false.

Let's override equals() method which will not follow above rules and throw NullPointerException and ClassCastException
@Override
   public boolean equals(Object obj) {
     Person person = (Person) obj;
     if(this.id.equals(person.id) && this.name.equals(person.name)){
        return true;
     }else{
        return false;
     } 
}
Let's test this code with all possible values i.e. by passing object of same class, null, object of different class. And see what it returns-
public static void main(String[] args) {
   Person p1 = new Person(1, "Prithvi");
   Person p2= new Person(1, "Prithvi");
   System.out.println(p1.equals(p2));//returns true
   System.out.println(p1.equals(null));// throws NullPointerException
   System.out.println(p1.equals(new Employee()));//ClassCastException 
}
When you run above code, exception will be thrown in last 2 lines.

Solution:

Let's rework on equals methods and fix the problem we have seen above-

@Override
public boolean equals(Object obj) {
 if (this == obj)
  return true;
 if (obj == null)
  return false;
 if (getClass() != obj.getClass())
  return false;
 Person other = (Person) obj;
 if (id == null) {
  if (other.id != null)
   return false;
 } else if (!id.equals(other.id))
  return false;
 if (name == null) {
  if (other.name != null)
   return false;
 } else if (!name.equals(other.name))
  return false;
 return true;
}
This method looks long but it covers all possible scenarios. Let us understand logic of above code -

  • First condition(this==obj) tests if two object references refers to same object, content will always be same. No need to compare content of objects
  • Second condition(obj==null) to fulfil above first rule. If null is passed to equals() method, it returns false. 
  • If two object types are different, third condtion: getClass() != obj.getClass(), return false instead of throwing ClassCastException.
  • Rest of code is self explanatory. It compares content of object.

Equals method's Contract:

There are certain equals method's contract. Below list of contract are directly pulled from Java docs-
  1. It is reflexive: for any non-null reference value x, x.equals(x) should return true.
  2. It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
  3. It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
  4. It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
  5. For any non-null reference value x, x.equals(null) should return false.
Note: We have already covered 5th point in rules above.

Complete Code:

Here is the complete code we discussed in this blog-

public class Person {
 private Integer id;
 private String name;
 public Person(Integer id, String name) {
  this.id = id;
  this.name = name;
 }
 
 @Override
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (getClass() != obj.getClass())
   return false;
  Person other = (Person) obj;
  if (id == null) {
   if (other.id != null)
    return false;
  } else if (!id.equals(other.id))
   return false;
  if (name == null) {
   if (other.name != null)
    return false;
  } else if (!name.equals(other.name))
   return false;
  return true;
 }


 public static void main(String[] args) {
  Person p1 = new Person(1, "Prithvi");
  Person p2=p1;
  Person p3 = new Person(1, "Prithvi");
  System.out.println(p1.equals(p2));
  System.out.println(p1.equals(p3));
  System.out.println(p1.equals(null));
  System.out.println(p1.equals(new Employee())); 
 }
}
Output:
true
true
false
false

Comments

Popular posts from this blog

Data types and Literals in Java

How to define Auxiliary Constructor in case class in Scala