In this article, we will check a few important points on overriding equals() method when we are using inheritance. We consider two scenarios:
- When semantics of equals changes in subclass
- When superclass determines the semantics of equals and semantics of equals doesn’t change in subclass
We will discuss the point with concrete examples for quick understanding.
Scenario 1 – Semantics of equals changes in subclass
Suppose, we have designed a class named Super as follows:
public class Super {
protected int v1;
protected String v2;
Super() {}
Super (int v1, String v2) {
this.v1 = v1;
this.v2 = v2;
}
// rest of the class
}
We have also designed another class Sub that extends Super as follows:
class Sub extends Super {
private int[] v3;
Sub() {
super();
}
Sub(int v1, String v2, int[] v3) {
super(v1, v2);
this.v3 = v3;
}
// rest of the class
}
Now we need to check equality of objects of classes Super and Sub based on state (values of the instance fields at a particular time point). For that purpose, we need to override Object’s equals() method both in Super and Sub classes. One important point is that subclass Sub will have a different semantics for equality. Two Super class objects are equal when the values of instance fields v1 and v2 are same. But two Sub class objects are equal when the values of instance fields v1, v2, and v3 are same. Moreover, we need to check the class of the given objects. Considering all these points, we can implement equals() method as follows:
public class Super {
protected int v1;
protected String v2;
Super() {}
Super (int v1, String v2) {
this.v1 = v1;
this.v2 = v2;
}
@Override
public boolean equals(Object otherObject) {
if (otherObject == null) return false;
if (this == otherObject) return true;
if (getClass() != otherObject.getClass()) return false;
Super other = (Super) otherObject;
return v1 == other.v1 && v2.equals(other.v2);
}
// rest of the class
}
public class Sub extends Super {
private int[] v3;
Sub() {
super();
}
Sub(int v1, String v2, int[] v3) {
super(v1, v2);
this.v3 = v3;
}
@Override
public boolean equals(Object otherObject) {
if (otherObject == null) return false;
if (this == otherObject) return true;
if (getClass() != otherObject.getClass()) return false;
Sub other = (Sub) otherObject;
return super.equals(other) && Arrays.equals(v3, other.v3);
}
// rest of the class
}
Important Note: Since semantics of equals changes in subclass, we need to check the class of the objects inside equals() method. If you are curious, you can explore this point further on your own. You can start from here.
Scenario 2 – Superclass determines the semantics of equals and semantics of equals doesn’t change in subclass
We will consider the same classes Super and Sub as designed above except the equals implementation. But in this case, we consider two objects of classes Super and Sub (one Super object and one Sub object OR one Super object and another Super object OR one Sub object and another Sub object) are equals if the value of the instance field v1 is same. One important point is that subclass Sub must not change the semantics of equals as defined by Super class. Therefore, we won’t allow Sub class to override the equals() method. Moreover, we don’t need to check the class of the given objects. Instead, we need to check whether the otherObject is an instance of Super class or not. Considering all these points, we can implement equals() method as follows:
public class Super {
protected int v1;
protected String v2;
Super() {}
Super (int v1, String v2) {
this.v1 = v1;
this.v2 = v2;
}
@Override
public final boolean equals(Object otherObject) {
if (this == otherObject) return true;
if (!(otherObject instanceof Super)) return false;
Super other = (Super) otherObject;
return v1 == other.v1;
}
// rest of the class
}
public class Sub extends Super {
private int[] v3;
Sub() {
super();
}
Sub(int v1, String v2, int[] v3) {
super(v1, v2);
this.v3 = v3;
}
// Sub class cannot override the equals() method
// since it is final in Super class. Here, the semantics
// of equals is determined by Super class.
// rest of the class
}
Important Note: Since semantics of equals is fixed in superclass, we should use instanceof test inside the equals method.
That’s it!
If you find any significant errors or want to give me some feedback, feel free to contact me at maliksanjoykumar[@]gmail.com.
Sanjoy Kumar Malik is an experienced software architect and technologist. He is passionate about Cloud Computing, Software Architecture, and System Design. Apart from technology and software, he is an avid LinkedIn networker. You can join his 5.5+ lacs supporters on LinkedIn.