Reputation: 40056
First, I am not asking for the difference between them.
I have an idea in mind and I would like more brain to verify, which seems able to take (most of) the pros of using both approach.
In brief, equals()
in Java usually looks like this.
public boolean equals(Object other) {
if (other == null) return false;
if (this == other) return true;
if (this and other is not of the same type) return false
// then actual equals checking
}
The problem lies in the "this and other is not of the same type" checking.
One stream of people prefer using if (! (other instanceof MyClass))
, which have the shortcoming of
MyClass
and has overridden equals()
, a.equals(b)
and b.equals(a)
will not be the sameequals()
, by declaring it finalThe other stream of people prefer using if (getClass() != other.getClass())
, which has the shortcoming of
MyClass
should be able to use in place of MyClass
.a.equals(b)
will be false if a
is of MyClass
while b
is of a child class of MyClass
, even the internal attributes are just the same.I have an idea in mind I would want people to verify.
This approach should
equals()
, results should still be symmetricA child class will still be able to be used as its parent type (in aspect of equals()
, and equals
is not overridden of course)
public class Parent {
@Override
public boolean equals(Object other) {
if (other == null) return false;
if (this == other) return true;
if (!(other instanceof Parent)) return false;
try {
if (other.getClass().getMethod("equals", Object.class).getDeclaringClass() != Parent.class) {
return other.equals(this);
}
} catch(Exception neverHappen){}
// do actual checking
}
}
The main idea is, if this
encountered a Parent
object, but that object's equals()
is not declared in Parent
(i.e. that object is a child class, with equals()
overridden, I will delegate the equals
invocation to the child class object (or may simply return false).
Is there any shortcoming of using this approach that I have overlooked? (I guess performance loss will be one concern, but I believe call of getClass().method("equals").getDeclaringClass()
should be quite cheap?)
Upvotes: 2
Views: 5572
Reputation: 2371
As stated in the comments, if the child class does super.equals(..)
, you would get a stackoverflow. To prevent this, you would end up rewriting the whole parents equals(..)
in each children, which is even worst design.
Somehow, it depends on how/why you implemented inheritance in the first place. Should a child be comparable to a parent ? Does the comparaison makes sense ? (edited)
If you override equals, then you're not respecting LSP, by definition.
If a child has more parameters (which means it overides the equals method), then it shouldn't be equal to its parent, and this is why we can compare using getClass() != other.getClass()
in the parent. If you want to use the parent class's equals()
in your child (so you don't have to rewrite it all), you wouldn't end up stackoverflowing; equals()
would just be false, because they weren't meant to be equal.
What if a child is comparable to its parent ? If you're respecting LSP, the child shouldn't have a different equals()
than his parent (ie: equals shouldn't be overriden), I guess. So the asymetric case shouldn't exists.
If a child is comparable to its parent, but has more parameters ? This is now your design, not respecting LSP, so it's up to you to see what meaning it does have in your context and act accordingly.
EDIT:@Adrian yes, "does symmmetry makes sense" was poor wording, I should have said "does the comparaison makes sense ?".
For your example, if you compare two child class with getClass()
and they use super with also getClass()
it will return true (the test will be redondant, because this.getClass()
and other.getClass()
will always have the same values, in the child and in the parent). But as I said, if you compare a child and a parent, it will be false (and I think it's normal if they have different parameters).
Why use final only on equals with instance of ? You said it, because of possible assymetry, whereas it's not possible to be assymetric with inheritance using getClass()
, so it's no use making it final in this case.
As a side note, if you use getClass()
, then multiple child of the same parent won't be comparable ( always return false ). If you use instanceof
, they can, but if they do, no child should override equals()
for risking of breaking symmetry. (I think you got this, but I was wondering when choose insteanceof
instead of getClass()
if it's so problematic).
Upvotes: 1
Reputation: 533660
Is there any shortcoming of using this approach?
It wouldn't work. The method to call is determined at compile time. Polymorphism only apply to reference before .
not after it.
other.equals(this); // always calls equals(Object); as `other` is an Object.
Also
other.getClass().getMethod("equals", /* method with no args */).getDeclaringClass();
I assume you wanted other.getClass().getMethod("equals", Parent.class) but you would have to catch NoSuchMethodException
I believe call of getClass().method("equals").getDeclaringClass() should be quite cheap?
Not in my option. It's quicker than reading something from disk, but you wouldn't want to do this very often.
Last but not least you should ensure that a.equals(b) ==> b.equals(a) and if a.equals(b) then a.hashCode() == b.hashCode() which is hard to do is a
and b
are different types.
Upvotes: 0