posted 17 years ago
Well, it's possible to allow subclasses to be equal to other subclasses, but it's usually inadvisable as the process is somewhat error-prone and likely to lead to confusing results. Generally in order to do this, you would need to require that all subclasses use effectively the same implementation of equals() and hashCode(). By "effectively the same" I mean that it would be preferable if they all inherited the exact same implementation from a single base class, which declares those two methods to be final. In some cases you might be tempted to make this base implementation nonfinal in order to allow subclasses to make implementations which are more efficient for that particular implementation. However the results of such methods are obligated to be the same as the base implementation results, or you will violate the base contract of equals() and/or hashCode().
As an example of a data type which allows different subtypes to evaluate as equal to each other, check out java.util.List. Since this is an interface, they can't provide a final implementation of equals() and hashCode() (and they decline to make the implementations in AbstractList final, thought they probably should have). However the APIs for these two methods in List do spell out fairly exact rules which all subtypes are required to follow, or lead to very confusing results.
Also, regarding your example of two Address types which are identical except that one is mutable while the other is immutable - personally I feel there is no good reason to use equals() or hashCode() on a mutable object if an immutable version is available. Mutable objects make lousy keys in Maps. Or rather, it's a horrible idea to ever change their values after they've been inserted into a Map. And so if an immutable version is available, why not use it? Much better I think. That, in my opinion, is why they didn't override equals() and hashCode() in StringBuffer. Why bother? Anyone wanting to evaluate equality of such objects is better off using immutable Strings instead. Not everyone agrees with this opinion of course. But still, the importance of having equivalent equals() implementations for mutable and immutable versions of what is otherwise the same datatype is... limited, at best. In my opinion.
"I'm not back." - Bill Harding, Twister