Re: How to Write an Equality Method in Java
Posted: Jun 1, 2009 2:59 PM
> It's really a shame when people cite LSP as if it were
> some kind of golden rule. In fact, it doesn't exist, and
> never has - it was cited as a 'wouldn't it be nice if'
> rule and is trivially disproven:
> The problem is that you can't, in fact, blindly substitute
> one thing for another hand have your program work in
> exactly the same way. Mocking is an explicit example of
> this, where you don't want your program to work the same
> way (but want to supply a different implementation that
> does nothing/is testable/doesn't have the setup costs
On LSP, I brought it up in the book chapter and article because I'd heard it as an objection to the
canEqual approach. So I wanted to put an "LSP defense" in the text to head this objection off at the pass. When I did, I looked up the original text and it also looked to me like Liskov was saying that subtypes must have exactly the same behavior as supertypes. But what the world thinks LSP means is that *at some level of abstraction" the behavior is compatible, or that the subtype behavior must fulfill the semantic contract of its supertype. I felt a bit uneasy saying the latter is what LSP means, because I also wasn't sure that's what Liskov meant, but it is what the general usage of the term LSP seems to mean.
> Unfortunately, far more people use Bloch's implementation
> (using instanceof) instead of one which preserves
> transitivity and obeying the contract as is.
> You can either (a) test for instance field equality (using
> getClass and friends) or (b) test behavioural equality
> using the results of property accessors, but mixing the
> two is self-inconsistent.
I'm not sure I understand your b. Or maybe your whole last paragraph. Can you clarify?
What you wrote in 2004 in the blog post you linked to is what I think has been the conventional wisdom in the Java community for a long time, although it wasn't necessarily widespread wisdom. You wrote:
Specifically, in order to rely on the LSP, you have to ensure that M> obeys the contract defined in T, which implies that it must be reflexive, symmetric and transitive. If you don't have that, you don't have a valid overridden method as per the LSP definition, so you can't even use that as your argument.
Lastly, if you do try and use
instanceof, you can guarantee that you break symmetry, because the
instanceof is asymmetric. I did point out in my proof on JavaWorld, that it is possible to overcome this behaviour; however, for any implementation of
instanceof that obeys reflexivity and symmetry will be forced to break transitivity.
So the arguments are set in stone. The LSP doesn't hold in Java, and never has.
--end of Alex quote
It is true that if you use
instanceof in the first two ways shown in this article, you either break symmetry or transitivity, and that if you use
getClass you can fulfill the
equals contract. This is basically what you're saying in the above quote. But what's been missing from the discussion is that there is a technique that lets you use
instanceof while at the same time maintaining the
equals contract, and that's what this article is trying to show. The benefit of
instanceof/canEqual over just using
getClass is that subclasses instances can equal superclass instances. The writer of the subclass decides whether this is possible based on whether or not he or she overrides the