|
Re: How to Write an Equality Method in Java
|
Posted: Jun 1, 2009 11:59 AM
|
|
Hi Alex,
> 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: > > http://alblue.blogspot.com/2004/07/java-liskov-substution-p > rinciple-does.html > > 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 > etc. > 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 .equals() using 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 canEqual method.
|
|