Oct 31 2005
More Bad Java News
In thinking about it more, I’m back to declaring the equals(Object) method completely screwed. The this.getClass().equals(them.getClass()) || them.equals(this) causes an infinite recursion if you have an instance of one subclass call it on an instance of a different subclass, neither of which override equals(Object).
Popularity: 2% [?]







I apologize for not responding to this sooner- I came down with a serious cold, and am only now dragging myself back into the land of the living.
There are four cases here, given
a.equals(b). The first is that a and b are the same class, in which the “obvious” compare should be done. The second is where a is a subclass of b- in this case, a’s compare should be used (a will know about b’s class, the other way around is probably not true). The third case is that b is the subclass of a, in which case equals should call b’s equals (b will know about a’s class). Note that the infinite recursion stops here- when a calls b’s equals, b’s equal will note that b is the subclass, and thus do the local compare. Alternatively, you could reverse the logic and call the less-specialized equals on the superclass.The interesting case is when a is not a subclass of b and b is not a subclass of a. If you’re willing to use the less specialized comparison, you could walk the inheritance trees of both classes looking for the common ancestor. This can be done in O(N) where N is the depth of the inheritance tree. Hint: starting at Object and working downwards is a good solution.
But I comment that our solution has grown signifigantly- both in the complexity of the code and in the performance and memory requirements. We went from a situation where we were doing 2 or 3 integer compares (very fast) to where we are implement an O(N) search on the inheritance trees of both objects for each comparison.
But even if you ignore this problem you can’t avoid some form of reflection in compare. This is just a fundamental problem of Java’s type system. Notice that the argument to equals is of type
Object. Same forCompareable.compareTo. You have to handle the case of comparing apples to oranges, and this basically mandates using reflection of some sort. Which is going to be a signifigant performance hit over the cost of a couple of 1 clock cycle integer (or even floating point) compares.The only way I can think of to avoid this problem is to duplicate code- with all the ills and problems that have caused that to be considered a deadly sin in modern programming. Write your own hashmap class, and copy it, with a global search/replace to hold new types. This is the solution my Dad uses. But even here you have to be carefull when comparing 3D points to 2D points.
I note that this problem can be reimplemented in Ocaml- but that by default the language discourages it (as opposed to Java, which requires it).
Commons-Lang and the equals()/compareTo() Debacle…
Like most traumatic realizations, I’ve been having trouble getting the fundamental equals/compareTo brokeness of Java out of my head.
I decided to take a look at the Commons-Lang library to see how they deal with it. They’ve got a class ca…