Equality is context sensitive. It’s very rarely as cut-and-dried as people think it is.
As a simple example, consider two $5 notes. I think everyone can agree that these notes have the same value – they are both worth $5. But are they equal?
No. They are two physically distinct objects. They will be in different physical condition. They have different serial numbers. Depending on the year of printing, they may have different patterns. In fact, they may even have different values, despite being the same denomination – one may be a rare note, and thus collectible. So the only context they are equal in is ‘face value’.
But what about other things with the same value? Are two $5 notes equal to one $10 note? Or ten $1 coins? Or a debit card with a $10 balance? I doubt many people would consider these to be equal, despite having the same value.
When, as a developer, we implement an
equals() method, we are choosing a default context for equality. There’s a good chance we won’t get it right for all circumstances. One of the reasons I suggest not implementing
equals() for mutable objects is that you automatically take time out of the equality context.
What should be done, instead, is that we should expose better methods that actually say what we mean. For example – the
Collection interface in Java defines equality, and does it badly. The implication is that
Collections are equal if they contain the same items – but Sets and Lists aren’t equal to each other.
Lists are equal if they contain items that are equal in the same order – but
LinkedHashSets can’t be equal to a
List even if they have the same items in the same order. But a
LinkedList and an
ArrayList must be equal – because a list is a list is a list, right? Perhaps the
Collection interface needs a
containsSame() method to complement the
containsAll() – and collections where order is important could implement an interface that provides
containsSameInSameOrder()? This would probably be better than an
(While we’re on the Collections API: note that collections that provide sort functions can all take custom
Comparators – because sorting is context sensitive. Yet no collection accepts a custom equality-checker; if you want to provide a custom context for equality, you need to build your own collection class, or decorate incoming objects)
Create fine-grained immutable objects, and implement equality checks there. Coarse-grained objects – even if they are immutable – probably shouldn’t bother with equality checks, because there will be too many contexts for “equality” to be consistently defined. And mutable objects shouldn’t bother, because you automatically leave out the temporal context.