The "lacks a no-arg ctor" is new and interesting. I think what you're
saying is that having an explicit no-arg ctor with an empty body is a
more stable signal of intent than an implicit one? I can see that.
Shouldn't the non-empty body one be "declares any constructor with a
non-empty body", regardless of arity? The only reasonable thing here
would be a side-effect (like an instance initializer) or a nontrivial
super call, both of which seem to put you in the Identity category.
Still not sold on keying off the existence of a constructor with a novel
shape at the classfile level. Seems too clever, and comes with a
complex verification.
On 9/2/2020 7:30 PM, Dan Smith wrote:
Summarizing, here's what I think we want:
---
Language features
java.lang.IdentityObject is a normal interface, can be implemented/extended by
any class or interface.
A non-inline class with any of the following properties implicitly implements
IdentityObject:
- Is concrete (java.lang.Object excluded)
- Declares a (possibly private) instance field
- Is an inner class with an enclosing instance
- Declares an instance initializer
- Lacks a no-arg constructor (explicit or implicit)
- Declares a no-arg constructor with a non-empty body (something other than
'super();')
- Declares a synchronized method
A warning encourages classes in the last four categories to explicitly implement
IdentityObject in order to ensure stable class evolution. (Possibly of the "this
will become an error in a future release" variety.)
(Note that I'm tentatively allowing constructor overloading in a
non-IdentityObject class. It's not generally useful, but is harmless and could
be useful in some special circumstances.)
It is a compile-time error if (among other things) an inline class:
- Implements IdentityObject, directly or indirectly
- Can't access its superclass's no-arg constructor
- Declares a synchronized method
---
JVM features
Traditionally, a class declares that it supports identity subclasses by declaring one or
more <init> methods. (Because with no <init> method, it's impossible to
initialize a subclass instance.)
Similarly, a class declares that it supports inline subclasses by declaring an
<init> method whose ACC_ABSTRACT flag is set. Invocations of that method are
no-ops. (Tentatively. We could encode this differently. The important metadata is i)
the class supports inline subclasses, and ii) access flags for inline subclasses.)
A class that is not inline and does not declare support for inline subclasses
implicitly implements IdentityObject.
A class that declares support for inline subclasses is subject to the following
constraints at class load time:
- Must be ACC_ABSTRACT (java.lang.Object excluded)
- Must not declare an instance field
- Must not declare a synchronized method
- Must not implement IdentityObject, directly or indirectly
- Must have access to extend the superclass (per the super's abstract <init>
method)
An inline class is subject to similar constraints at class load time:
- Must not be ACC_ABSTRACT and must be ACC_FINAL
- All fields must be ACC_FINAL
- Must not declare a synchronized method
- Must not implement IdentityObject, directly or indirectly
- Must have access to extend the superclass (per the super's abstract <init>
method)
---
API features
(Optionally) The method Class.Interfaces(), and similar reflection API points,
filters out IdentityObject when it is not explicitly named in the class file.