I think Ari defines a concept that is neboulus to begin with, then he proceeds to show that the definition is troublesome. Go figure :-) That is a cheap parlour trick that no one falls for. However, the discussion about equals and so on is interesting.
Show of hands here, when did you last implement equals, compareTo or hashcode. Did you do it consistently?
I do it sometimes
, not nearly enough as often as I should. But that is probably something we all know. But what I am not sure about is how you do it.
Take
equals
it should be reflexive, symmetric, transitive and consistent for non null values. This is often not a problem, usually there is a String member or something like that you can delegate to and do the same with hashcode. But what I am not sure about is how to handle mutable objects. Should they generally delegate to the default Object equals? I think so.Then should
compareTo
follow the same rule of thumb? Do not implement it for mutable objects?I am not sure, probably. It seems cleaner to implement a comparator for mutable objects and let the comparator handle the dirty stuff if objects mutate during a sort. Most probably the only sane way to deal with that situation is to ignore it :-)
By the way, my friend still has not started a blog.
Two comments: first, equals is misguided in the same way that toString is - equals in which sense? There are more ways of comparing two things, and which way of looking at things you use should probably be explicit. Using named comparators allows for this.
ReplyDeleteSecond: I've never seen anyone write a correct implementation of equals other than by accident. I've never done it myself either. Unless your class is declared final, then you have to take into account that an instance of the class you're writing may be compared to an instance of a subclass of it, and then the fields that the author of the subclass considers important have to be taken into account, or your equals is not symetric. You can't just add other.equals(this), since that would lead to infinite recursion. The only way I know of is to have declare extra one-sided equals and then have your equals do this.oneSidedEquals(other) && other.oneSidedEquals(this). But I've never seen anyone actually do that, so all classes that arent' declared final (for some unrelated reason)and which do define equals are broken.
I didn't think about sub-classing. It surely makes equals very difficult since it should be symmetrical and transitive. Perhaps the only sane interpretation of equals is to say it actually means the same instance of a Class in this JVM. That is, keep the default implementation as it is in Object and declare Object.equals final.
ReplyDeleteA looser interpretation might be to say that when comparing instances of Class and SubClass you upcast to the least common denominator (Class) and use the implementation of equals on Class. So you implementation of equals in SubClass should take into account that it has inherited an equals from Class and if the other is an instance of Class it should use that equals-implementation instead of it's own.
As Magnus says: I've never seen a correct implementation either, although I have many times thought that I have written one :-)
Palantir has some things to say about this: http://blog.palantirtech.com/2007/09/05/getting-equals-and-hashcode-right/
ReplyDelete