Equality operator for identityless classes
Kevin Bourrillion
kevinb at google.com
Wed Nov 3 15:58:17 UTC 2021
I imagine we might be constrained to this design by the need to support
compatible migration. So there may be nothing we can do.
But there is a pretty serious problem here.
Background: code like IdentityHashMap, which cares about *objects per
se *instead
of what those objects *represent*, is unusual, special-case, egghead,
lift-the-caution-tape code. It is not normal. It's surely more common in
JDK code. But I strongly suspect that the vast majority of `==` tests in
the wild are not expressing questions of identity at all, but are
abbreviations for `equals()` when the developer happens to believe it's
safe. Many of those are of course bugs, and then there are plain accidental
usages as well.
Today, things are pretty okay because developers can learn that `==` is a
code smell. A responsible code reviewer has to think through each one like
this:
1. Look up the type. Is it a builtin, or Class? Okay, we're fine.
2. Is it an enum? Okay, I resent having to go look it up when they could
have just used switch, but fine.
3. Wait, is this weird code that actually cares about objects instead of
what they represent? This needs a comment.
The problem is that now we'll be introducing a whole class of ... classes
... for which `==` does something reasonable: only the ones that happen to
contain no references, however deeply nested! These cannot at all be easily
distinguished. This is giving bugs a really fantastic way to hide.
I think we'd better consider some heretical options, like introducing `===`
and `!==` as sugar for Object.equals(). It seems tragic to imagine the
entire world (except the special-case code) transitioning over to that, as
it's quite ugly. But it would lead to more correct code. Maybe you have
other ideas.
On Wed, Nov 3, 2021 at 7:05 AM Brian Goetz <brian.goetz at oracle.com> wrote:
Extrapolating, ACMP is a _substitutability test_; it says that
> substituting one for the other would have no detectable differences.
> Because all objects have a unique identity, comparing the identities is
> both necessary and sufficient for a substitutability test.
What you say here may be technically true, but people who override equals()
are already trying their best to disavow identity in the only way they
have. And that makes your statement here actually kinda *wrong*. Being a
necessary and sufficient substitutability test is literally, exactly, what
Object.equals() does (and never mind that people might implement it *wrong*).
If that method's purpose is not to give classes control over their own
substitutability test -- which they *need!* -- then I can't imagine a
purpose for it at all. (And yes, those objects still do expose identity,
but their equals() implementation is consenting to have that identity
"forgotten" at any time just by round-tripping it through some collection
etc.)
--
Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com
More information about the valhalla-spec-observers
mailing list