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.


Reply via email to