I have a simple base class, which is later extended by many separate classes, which potentially introduce new fields, but not necessarily. I defined an equals method in the base class, but also overriden that for a few subclasses. Is it OK to mix definitions in base/subclasses? In my case it was to avoid code duplication checking the same fields.
-
6Can you explain what you mean by mixing definitions. Thanks. – tjg184 Oct 31 '12 at 16:10
-
Having one definition in base class, which might/might not be overriden. I meant to say "mixing approaches of base class defines vs subclass defines" – Bober02 Oct 31 '12 at 16:15
8 Answers
Take a look at "Implementing equals() To Allow Mixed-Type Comparison" from Angelika Langer .
Here is a brief explanation of some problems and a possible solution:
The equals contract says (amongst others):
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.
That means you might get problems if your sub class is introducing new fields and you're comparing an object of the base class (or another sub class that doesn't override equals) to an object of this sub class.
Do NOT do the following:
class BaseClass {
private int field1 = 0;
@Override
public boolean equals(Object obj) {
if (obj instanceof BaseClass) {
return field1 == ((BaseClass) obj).field1;
}
return false;
}
}
class BadSubClass extends BaseClass {
private int field2 = 0;
@Override
public boolean equals(Object obj) {
if (obj instanceof BadSubClass) {
return super.equals(obj)
&& field2 == ((BadSubClass) obj).field2;
}
return false;
}
}
because you get
BaseClass baseClass = new BaseClass();
BadSubClass subClass = new BadSubClass();
System.out.println(baseClass.equals(subClass)); // prints 'true'
System.out.println(subClass.equals(baseClass)); // prints 'false'
A possible solution:
Replace the instanceof
-check with a class comparison:
obj != null && obj.getClass() == getClass()
With this solution an object of BaseClass
will never be equal to an object of any subclass.
If you create another SubClass
without an @Override
of the equals
method, two SubClass
-objects can be equal to each other (if the BaseClass.equals
check decides so) out of the box, but a SubClass
-object will never be equal to a BaseClass
-object.
A good implementation could be as follows:
class BaseClass {
private int field1 = 0;
@Override
public boolean equals(Object obj) {
if (obj != null && obj.getClass() == getClass()) {
return field1 == ((BaseClass) obj).field1;
}
return false;
}
}
class GoodSubClass extends BaseClass {
private int field2 = 0;
@Override
public boolean equals(Object obj) {
if (obj instanceof GoodSubClass) {
return super.equals(obj) && field2 == ((GoodSubClass) obj).field2;
}
return false;
}
}
Please refer to the article mentioned above for more advanced problems and their solutions.
-
1Thanks for the Angelika Langer article link. Though this violates the Liskov substitution principle, AFAIK. See again "Effective Java" by Joshua Block. (I haven't read the full article yet, though.) – Puce Oct 31 '12 at 23:53
-
2
-
@Mukul I think it's actually better because you avoid the need to override the equals method if you don't do additional checks plus you can call `super.equals()` without breaking everything. I just submitted an according edit – Qw3ry Aug 24 '17 at 09:57
-
In `GoodSubClass`'s `equals()`, do we need to check for `obj instanceof GoodSubClass`? Isn't `super.equals(obj)` already covering that? – TheCommonEngineer Mar 26 '21 at 11:49
No, it's not possible to conform to the equals contract when introducing new fields which are relevant to the equals method. See "Effective Java" by Joshua Bloch for more information.
Edit:
I don't have the book at hand right now, but I think it's ok if the base class is abstract/ cannot be instantiated.

- 37,247
- 13
- 80
- 152
-
-
It's not possible with inheritance but possible with a composition (stated in the same item of Effective Java) – Farid Oct 19 '19 at 06:41
While the following doesn't handle every case, I've found it to be quite practical. I've used this many times when I have both a SuperClass and a SubClass in play. I don't want to inter-compare them, but I also don't want to re-implement all of SuperClass equals() for SubClass. It handles:
- a.equals(b) == b.equals(a)
- Does not duplicate field comparison code
- Easily generalized for any subclass depth
- Subclass.equals(SuperClass) == false
- Superclass.equals(SubClass) == false
Code example
// implement both strict and asymmetric equality
class SuperClass {
public int f1;
public boolean looseEquals(Object o) {
if (!(o instanceof SuperClass)) return false;
SuperClass other = (SuperClass)o;
return f1 == other.f1;
}
@Override public boolean equals(Object o) {
return looseEquals(o) && this.getClass() == o.getClass();
}
}
class SubClass extends SuperClass {
public int f2;
@Override public boolean looseEquals(Object o) {
if (!super.looseEquals(o)) return false;
if (!(o instanceof SubClass)) return false;
SubClass other = (SubClass)o;
return f2 == other.f2;
}
// no need to override equals()
}

- 3,157
- 1
- 23
- 36
You could use the super()
method to call the method of class that you're extending to prevent any need of code duplication
public class BaseClass {
public boolean equals(BaseClass other) {
return (other.getBlahblah() == this.Blahblah && .....);
}
}
public class DerivedClass extends BaseClass {
public boolean equals(DerivedClass other) {
return (super(other) && other.getNewAttribute() == this.NewAttribute.....);
}
}

- 4,370
- 5
- 28
- 43
-
1+1 for advice to call super first. One note - it's not a valid Java code, it would be cool to correct it (i understand it may be just a pseudo code as an example). – Andrey Mormysh Oct 31 '12 at 17:18
-
Thanks. I just mashed it out without doing checks on most stuff. Its been a little while since I've been doing Java and must've meshed some other syntax and calls inside it. – Grambot Oct 31 '12 at 17:37
-
Quite a valid approach. The issue is in one of your subclasses it must retain the definition of equals as is bound by its parent. Else you have a broken equals function, which can cause some very unique scenarios during run time.

- 23,987
- 16
- 94
- 151
I guess, That's perfect to provide the equals(Object obj)
and hashCode()
method implementation in super
class
as Java
did. We all know that Java provide the hashCode() and equals(Object obj)
method implementation in the base class java.lang.Object, and when ever required we override
them in our class
.

- 6,136
- 12
- 51
- 73
If you do not write you code correctly it will creates a serious problem, called asymmetry (violates contract for equality) so lets see our options.
Best Practice – Same Classes Strategy. If B is a subclass of A and each class has its own equals method, implemented using the same classes strategy, then the class B should be declared final to prevent the introduction of an asymmetric definition of equals in any future subclass of B.
Question. What if we don’t wish to make B final?
Use Composition instead of Inheritance. Whenever classes B and A, where B is a subclass of A, require different equals methods, using composition instead of inheritance is a good strategy, and if making the class B final is not an option, it is the only safe way to handle equals.
How?
public class A{
public boolean equals(Object ob){
//write your code here
}
}
class B{
A a= new A();
public B(A a){
this.a= a;
}
public boolean equals(Object ob){
//...write your code here
if(!((B)ob).a).equals(a)) return false;
//...write your code here
}
}

- 81
- 1
- 5
I think it is perfectly fine as long as you follow eqauls() and hashcode() contracts.

- 65,990
- 13
- 130
- 167