I have a question about the equals method. Typically, I implement the equals method as follows (assume I have some class A with a property called score):
Generally, this works fine. But what happens if I have class B that extends A and B's equals method also checks an attribute called time. Now if I call A.equals(B) where A and B have the same score, this will return true. But if I call B.equals(A), A is not an instanceof B, and thus the method will return false.
This violates the principal that equals is symmetric. The only alternative I see is by checking the actual class name, and not using instanceof, but this does not seem like an elegant solution.
Can someone comment on this and let me know if my equals is incorrect or if this is expected behavior of equals?
[corrected position of the closing code tag - Ilja] [ March 07, 2007: Message edited by: Ilja Preuss ]
checking the actual class name, and not using instanceof, but this does not seem like an elegant solution.
Nothing wrong with using class. If I remember correctly, this sort of thing ought to work:- No use of instanceof, which the Bloch article quoted earlier shows is unreliable in this situation. You just have to make sure that all the fields which are reference types support the equals() method, rather than relying on the equals() method inherited from Object.
And less advanced readers are reminded to override the hashCode() method too. [ March 07, 2007: Message edited by: Campbell Ritchie ]
Yup! The Effective java book is great. Try composition is such scenarios.
Joined: Oct 09, 2006
Thanks everyone for all of the input. Those articles were really interesting and pose some good questions. It seems like either way you do the equals method, there are some pros and cons. For my purposes, I'm going to stick with the instanceof operator.
I didn't follow those links today, but I recall from prior pondering that it seems unwise (a nice word for wrong) to ever have two objects of different classes equals(). I have made some special case comparators that found objects of different types equal but that was an external evaluation, not a property of the objects. Wow, that felt like lawyer talk. Did it make sense?
A good question is never answered. It is not a bolt to be tightened into place but a seed to be planted and to bear more seed toward the hope of greening the landscape of the idea. John Ciardi
Just be sure to pick a nice shade of paint for that corner you will be looking at it from
I been there enough times. Sometimes I even went there in defiance. But I'm too old now
I have used name based equality when comparing classes that may have been loaded from different classloaders. In the end I got scared. Maybe paranoid. Code shock?? I figured out another way...
Joined: Jan 17, 2006
Originally posted by Stan James: I didn't follow those links today, but I recall from prior pondering that it seems unwise (a nice word for wrong) to ever have two objects of different classes equals().
In general I agree however I do think there are special cases:
To me these should not only be considered equal but should evaluate to the same hash code. I only mention this because I've been trying my hand at a poker odds calculator, and the fastest of these use very large lookup tables to evaluate the strength of a hand. If I have sorted lists of cards in a hash set and I want to check if the hash set contains the list of cards I have in my hand, I don't want to worry about whether that list was created using a LinkedList, or ArrayList, or from Arrays.asList(), I just want an O(1) lookup that is not constantly surprising me or forcing me to think about the implementation details.
And that's exactly what the List interface mandates. Set and Map work similarly. This can lead to strange results though, as it means that SortedSet and SortedMap can't really override these definitions without creating bigger problems. As a result, two TreeSets must be considered equal if they have the same contents, even if the sort order is different (because they use different Comparators). That's... weird, at best. So while I agree with Garrett that there are times it seems to make sense for different List implementations to evaluate as equal... there are also times this doesn't make sense.
Fortunately, any time you don't like the way that equals() has been implemented for a particular class, you can always make a new wrapper class that uses a completely different implementation. In the case of Garrett's example with evaluating poker hands, if List hadn't already been defined in a way compatible with his needs, he could have simply introduced a new class (or interface) - PokerHand or CardSet or whatever - and defined the equals() method accordingly. So in case of trouble, there's usually a workaround available. But it's important to be aware of the potential for problems with interfaces like List, Set and Map, attempting to impose a single definition of equals() on all implementations. [ March 07, 2007: Message edited by: Jim Yingst ]