IdentityObject and InlineObject

John Rose john.r.rose at oracle.com
Thu Apr 9 21:38:42 UTC 2020


On Apr 8, 2020, at 9:54 AM, Brian Goetz <brian.goetz at oracle.com> wrote:
> 
> This is a good time to review the motivations for Identity/InlineObject, and see if there's anything we want to tweak about it.  
> 
> There are two main branches of the motivation here: pedagogical and functional.  
> 
> Pedagogically, we're asking users to amend their view of "object" to allow for some objects to have identity, and some objects to not have identity, and supertypes are one of the prime ways we capture these sorts of divisions.  It's not an accident that we have both the informal statement "everything (except primitives) is an object" and the hierarchy constraint "all classes extend (implicitly or not) java.lang.Object".  Not only is Object a place to hang the behavior that is common to all objects (equality, etc), its position at the root of the hierarchy sends a message that conditions how we think about objects.  The intent of partitioning Object into IdentityObject and InlineObject is an attempt to capture the same.

(hear hear!)

These days I visualize an object’s “identity stuff” as a unique
(number-like) token that is (in some fuzzy sense) stored in the
object’s header, and participates in all requests for identity-specific
operations.  These include:

 - acmp/identityHashCode
 - synch/wait
 - sitting in a WeakReference
 - side effects into fields (plus JMM consequences)

If an object doesn’t have that extra token, it cannot do any
of the above operations.  (Well, some like acmp/iHC might
have fixups, but they don’t work the same way, and users
may see the consequences bubble up.)  For sure, that token
and its operations sounds like an O-O supertype.  And the
*lack* of the token sounds like a super of that super,
which turns out to be Object (viewed restrictively).

> Functionally, there are operations that apply only to identity objects, such as identity equality, synchronization, Object::wait, etc.  Some of these have been totalized appropriately (such as `==`); others are partial.  Having synchronization be partial, without offering developers a way to express "if I tried to synchronize on this thing, would it throw", just makes Java less reliable, so we want a way to express identity both in the dynamic type system (`instanceof IdentityObject`) and the static type system (`<T extends IdentityObject>`).  
> 
> We also thought, at one point in time, that InlineObject and IdentityObject would be a sensible place to put new methods or default implementations of Object methods.  

I have thought for a long time that we might want to endow
inline objects with lots of special furniture just for them,
like toString and equals.  (You can see this in my earliest
blogs on value types.)  Now that Record is coming out, I
see that such furniture can be factored in better ways,
not tied directly to value-ness.

> However, as the design has evolved, the need for this has gone away.  This opens the door to a new possibility, which I'd like to evaluate: just have one of them.  (And, if we only have one, the move is forced: IdentityObject.)  
> 
> In this world, we'd just have IdentityObject, which would be viewed as a refinement of Object -- "Object, with identity".  Identity classes would implement it implicitly, as today.  The pedagogy would then be, instead of "there are two disjoint kinds of Object", be "Some objects are enhanced with identity."  You'd still be able to say
> 
>     x instanceof IdentityObject
> 
> and
> 
>     void foo(IdentityObject o) { ... }
> 
> and
> 
>     class Foo<T extends IdentityObject> { ... }
> 
> as a way of detecting the refinement, but not the opposite.  So the questions are:
> 
>  - Pedagogically, does this help move users to the right mental model, or does the symmetric model do a better job?
>  - Functionally, is there anything we might do with InlineObject, that we would miss?  

As a weaker version of this question, are there any types which
authors of inline classes would like to opt into, in order to reify
important contracts commonly found on inline classes?

Here’s the main contract, I think:  “I promise that I won’t give
you race conditions.”  (N.B. This promise comes in two forms,
shallow and deep.  Let’s just stay shallow for now; adding deep
immutability is a little easier once the shallow version is defined,
but the reverse does not hold.)

Would InlineObject be a proxy for such a contract?  Yes, sort of,
but it would also (like Cloneable and Serializable) leak badly into
APIs that are studiously trying to avoid making promises of that
sort, or may be making them in other forms (e.g., implementation
internal mutable caches transparent to the client).

Possibly, we could define an interface ShallowlyImmutable
which pertains (at least potentially) to *all* inlines, and also
to all inline-like identity classes, notably our friends the
all-final-non-static-field classes.  It could be automatic on
records.  It could even be automatic on inlines, except maybe
then we’d want users to be able to opt out of it (for arcane
reasons—the inline object’s implementation logically includes
mutable state which the designer has declared is shallowly
local to the logical object).  In the same circumstances, it
could be automatic (with an opt-out) on friendly all-final
identity classes.  And so on.  Is this a contract worth
advertising?  Don’t know, but it’s a possibility.  And
(here’s the interesting part for today) it is *not* exclusive
to inlines, and perhaps not even *inclusive* of inlines.
Yet it correlates with them.

> Secondarily: 
> 
>  - If we take this step, is `IdentityObject` still the best name?

FTR, I like the terminology (which is hard-won by weeks of
discussion) of “inline” vs “identity”.  The latter term suggests
that the affected object has some mysterious “identity” property
hanging about it (that fuzzy token in the header I mentioned).
The term “inline” has no such connotation, and instead feels
adverbial—it’s a mode of use.  It’s perfect don’t change please…

The word Object is pretty annoyingly redundant, but IdentityObject
is (as Doug might say) not too terrible.  I also like just plain Identity
(but it will clash) and also Object.Identity (feels a little like Map.Entry).

— John


More information about the valhalla-spec-observers mailing list