Don't Let Hibernate Steal Your Identity
Subject:   Instanceof in equals()
Date:   2006-09-13 23:55:52
From:   PeterBecker
I liked the article as it goes straight into the direction of my own crusade -- people just don't think about object identity enough.
One complaint, though: you should never use instanceof in an implementation of equals(). That will nearly always break symmetry of the equals() relation if a subtype is introduced, since you will find situations where sub.equals(super) is false despite super.equals(sub) being true. The exception where you don't get that is when the relevant fields can not be the same in a subtype and a supertype instance, which seems actually intended in your example -- but you never really know that someone doesn't find a way to subtype your class by introducing a new persistance layer or some other indirection.
Sometimes people claim that not using instanceof would break Liskov's Substitution Principle. That necessarily means they haven't read the relevant paper(s) since Liskov and Wing actually discuss the topic of equality themselves and propose to introduce multiple equality functions. In addition breaking the specification of the equals() method on Object is exactly the type of change that breaks the LSP -- symmetry of the method is trivially provable from the specification, and that is what the LSP is about: Liskov and Wing do not consider extensional arguments at all.
Reference for that last bit:
It's just another part of that crusade of mine :-)
Full Threads Oldest First

Showing messages 1 through 2 of 2.

  • Instanceof in equals()
    2006-09-14 09:58:33  jbrundege [View]

    I don't agree with the idea that you should never use instanceof in an equals() method. You have to test the class of the given object somehow, and the two techniques are instanceof and getClass(). instanceof allows subclasses to inherit the equals() method, but with the danger that someone will override equals in a subclass which breaks the symmetric property of equals(). The alternative is to use getClass(), which only compares the runtime types and does not consider subclasses equal to their superclasses. This has the inverse danger when the author of the subclass intends to inherit equals() and is surprised to find that their subclass can never be equal to an instance of the parent class. This is a somewhat contentious issue. Josh Block has written about it here:

    The point of Liskov's Substitution Principle is that people who subclass must be careful to honor all the contracts of the parent class. My intention of the contract of PersistentObject was that the id is a unique identifier of object identity and should thus be used as the sole indicator of identity in the equals() method. The only way to break symmetry is to break this contract. Since my intent was that there is never a need a to override equals(), I could have made it final as well.

    An alternative would be to do the comparison with getClass(), which changes the contract to: object identity is defined as the id AND the class of the object. This works but is somewhat less flexible. Either way an author of subclasses must understand these contracts and make sure their subclasses honor them.

  • Instanceof in equals()
    2006-09-14 00:00:55  PeterBecker [View]

    Can someone please fix the parser to recognize a break even without a space? Or at least give an option to preview or reedit your post. To be fair: I should have read that line about linebreaks being maintained, which means I didn't need these extra breaks -- but sometimes habits are stronger than documentation, esp. when the latter seems to support your expectation on a first glance. :-)