I am overriding the equals() method in a Java class and found a conundrum that I can't explain.
The standard equals() contract states this:
- It is reflexive: for any reference value x, x.equals(x) should return true.
- It is symmetric: for any reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
- It is transitive: for any 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.
- It is consistent: for any 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 object is modified.
- For any non-null reference value x, x.equals(null) should return false.
Therefore, the standard equals() method would be constructed like so:
public boolean equals(Object other) {
if (null == other) return false;
if (this == other) return true;
if (!(other instanceof MyClassName)) return false;
MyClassName that = (MyClassName) other;
return this.myMemberVariable.equals(that.name);
}
Given class Foo and and class Bar extends Foo, both with a member variable String baz, the standard equals() method inside Foo looks like:
public boolean equals(Object other) {
if (null == other) return false;
if (this == other) return true;
if (!(other instanceof Foo)) return false;
Foo that = (Foo) other;
return this.baz.equals(that.name);
}
Whereas the standard equals() method inside Bar looks like:
public boolean equals(Object other) {
if (null == other) return false;
if (this == other) return true;
if (!(other instanceof Bar)) return false;
Barthat = (Bar) other;
return this.baz.equals(that.name);
}
If I had objects Foo foo = new Foo("Test"); and Bar bar = new Bar("Test");, and I called foo.equals(bar), this would return true. If I called bar.equals(foo), by the symmetric clause of the equals() contract, this is broken because inside equals() of Bar, (!(other instanceof Bar)) is true, making Bar's equal() method return false, which isn't correct, it should be true, by logic (both objects String baz are equal to each other), and by the symmetry clause, where if x.equals(y), y.equals(x) is inherently true.
I'm aware of the getClass() vs instanceof argument for overriding equals() and don't want another one of these arguments.
This brings me to my actual question. In such a case, how does one properly override the equals() method that follows the standard Java contract for equality?