Overriding Proper Java equals method

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?

Answers


According to your question

  • Foo can equal Bar if they have the same baz
  • Foo can equal Foo if they have the same baz
  • Bar can equal Bar if they have the same baz

This clearly shows that Foo and Bar will have the exact same implementation of equals(). Therefore, you should not override it at all.

If you were to try to ignore this and override it anyway, you would reach one obvious conclusion: you can't downcast a Bar to a Foo, but you can cast both arguments to Bar. As soon as you do that, you will realize that you could have simply used .equals() from Bar.


Need Your Help

Angular Node - send token to server upon opening new browser window

angularjs node.js token passport.js jwt

The question is pretty self-explanatory: I have a token stored on my browser. When I log in, the server-side authentication middleware uses Passport to authenticate me. However, when I close and re...

apache/python : file upload not working as expected

python html wsgi

I am working on apache/python2.7 and trying a simple file upload.