On 11/3/2021 3:00 PM, Kevin Bourrillion wrote:
Okay, let's stick a pin in proper-value-types (i.e. try to leave them out of this discussion) for a moment...

One question is whether the existing design for the bifurcated type hierarchy will carry right over to this split instead. (My understanding of that design is: every (non-Object) concrete class will implement exactly one of two disjoint interfaces, explicitly or not.)

My first thoughts were that the situation is different here: exposed identity seems to be strictly (maybe?) contractually stronger than no exposed identity. So here, a class being "noncommittal" /ought to/ look the same as it being identityless. In theory, it should be harmless for an identity class to extend an identityless class (while the opposite direction is a problem).

So, first, is that even right?

We went back and forth on this a few times.  A useful lens is to ask: how might we depend on reflecting identity-{ful,less}ness in the hierarchy?  These include:

    if (x instanceof IdentityObject) { ... }

    void m(IdentityObject o) { ... }

    <T extends IdentityObject> m(T t) { ... }

It is worth noting that the first is invertible (we can negate the condition) but the latter two are not.  Which is another way to say that, if anyone, anywhere, might want to write code that *requires* no identity, then we should consider giving them a way to do it.

(Ideally, if you're planning on (say) synchronizing on a parameter, you should engage the type system to ensure that an identityful object is passed; this is a good use of the type system.)

Next, even if so, the Backward Default Problem strikes again. To make a class identityless you would seem to need all your /supertypes/ to be, first! That's hard to pull off. And `Object` itself would seem to want to be marked identityless, which is obviously weird/problematic.

The superclass chain is tricky, but we've spent a lot of time shaking this box.  Some types are _identity-agnostic_.  These include interfaces that do not extend PrimitiveObject, abstract classes that meet some set of conditions, and Object.  The supertypes of a primitive class (and of an identity-agnostic class) must be identity-agnostic.

This is powerful.  For example, an interface could extend IdentityObject, which would effectively prohibit primitive classes from implementing it.  This is a way to signal "my (concrete) subtypes need identity."

Reply via email to