Implementing equals method using compareTo
Asked Answered
C

2

13

General question: When implementing an override of the default equals method in Java, what concerns should I have about simply utilizing an already implemented compareTo method vs writing independent logic into the equals method? I noticed someone mention in another question that foo.equals((String)null) returns false whereas String.compareTo((String)null) throws a NullPointerException. What makes these inconsistent results ideal functionality?

Sample equals method:

@Override
public boolean equals(Object obj) {
    if (obj != null && obj instanceof MyClass) {
        MyClass msg = (MyClass)obj;
        return this.compareTo(msg) == 0;
    }
    return false;
}

Edit: Quote from documentation on Comparable

The natural ordering for a class C is said to be consistent with equals if and only if e1.compareTo(e2) == 0 has the same boolean value as e1.equals(e2) for every e1 and e2 of class C. Note that null is not an instance of any class, and e.compareTo(null) should throw a NullPointerException even though e.equals(null) returns false

Edit:

After further review, I find it of note that the Comparable documentation also states the following:

The implementor must ensure sgn(x.compareTo(y)) == -sgn(y.compareTo(x)) for all x and y. (This implies that x.compareTo(y) must throw an exception iff y.compareTo(x) throws an exception.)

Ergo, since null.compareTo(x) obviously throws a NPE, x.compareTo(null) should throw a NPE as well. Whereas for equals, that is not necessarily the case. I am pretty big on the proper handling of NPEs, so I find this relatively significant.

Cavell answered 29/5, 2013 at 14:36 Comment(2)
Note that while, ideally, .compareTo() should be consistent with .equals(), this is by no means a guaratee. See BigDecimal for instance. And yes, null handling if you go that route can be a problem.Modality
Double.NaN has surprising behavior for both equals(Object) and compareTo(Double). NaN.equals(NaN) is true, but NaN == NaN is false. Similarly, NaN is the greatest Double in compareTo calls, and NaN.compareTo(NaN) == 0. equals is consistent with compareTo, but the Double object does not follow IEEE standards in this case. Necessary, but surprising.Silvers
L
7

The difference between equals() and compareTo() is that equals() just checks if two objects are equal to each other where the compareTo() is used to identify the natural order of the instances of specified class. Also equals() method has a contract with hashCode() method but compareTo() hasn't.

According to JavaDoc:

Note that null is not an instance of any class, and e.compareTo(null) should throw a NullPointerException even though e.equals(null) returns false.

It is strongly recommended, but not strictly required that (x.compareTo(y)==0) == (x.equals(y)). Generally speaking, any class that implements the Comparable interface and violates this condition should clearly indicate this fact. The recommended language is "Note: this class has a natural ordering that is inconsistent with equals."

You can feel free to reuse compareTo() method logic in your equals() method but keep in mind all the contracts to the equals(), hashCode() and contract from JavaDoc for compareTo() method. If they do not conflict with each other then go ahead.

I think that the enforcement of contracts is more important point.

Lemuel answered 29/5, 2013 at 15:4 Comment(2)
"I think that the enforcement of contracts is more important point. " <-- I can definetly agree with you there. Thanks for highlighting that these methods do indeed have distinguished intended functions.Cavell
While performance is always a valid concern, enforcing contracts looks to save me from writing more test cases in the long run. Thanks me for turning me onto that little nugget.Cavell
R
10

compareTo may involve a lot more work than is necessary just to get an equality answer, which may end up being a performance issue, depending on your application usage.

Other than that, following DRY principles it would be a good idea to re-use the code as you have suggested.

Rohde answered 29/5, 2013 at 14:40 Comment(1)
Nice note about potential performance. For all my blustering about NPEs, I overlooked that my proposed code already handles them correctly assuming they are done properly in compareTo. The only remaining concern then is performance.Cavell
L
7

The difference between equals() and compareTo() is that equals() just checks if two objects are equal to each other where the compareTo() is used to identify the natural order of the instances of specified class. Also equals() method has a contract with hashCode() method but compareTo() hasn't.

According to JavaDoc:

Note that null is not an instance of any class, and e.compareTo(null) should throw a NullPointerException even though e.equals(null) returns false.

It is strongly recommended, but not strictly required that (x.compareTo(y)==0) == (x.equals(y)). Generally speaking, any class that implements the Comparable interface and violates this condition should clearly indicate this fact. The recommended language is "Note: this class has a natural ordering that is inconsistent with equals."

You can feel free to reuse compareTo() method logic in your equals() method but keep in mind all the contracts to the equals(), hashCode() and contract from JavaDoc for compareTo() method. If they do not conflict with each other then go ahead.

I think that the enforcement of contracts is more important point.

Lemuel answered 29/5, 2013 at 15:4 Comment(2)
"I think that the enforcement of contracts is more important point. " <-- I can definetly agree with you there. Thanks for highlighting that these methods do indeed have distinguished intended functions.Cavell
While performance is always a valid concern, enforcing contracts looks to save me from writing more test cases in the long run. Thanks me for turning me onto that little nugget.Cavell

© 2022 - 2024 — McMap. All rights reserved.