Superclasses for inline classes
Brian Goetz
brian.goetz at oracle.com
Thu Feb 6 17:53:25 UTC 2020
I've been thinking about this, and gone around in circles a few times.
The properties laid out here are understandable and easily checkable at
both compile and run time. (I might add "no synchronization", or at
least "no synchronized methods".)
I agree that having some sort of class-level modifier gives this concept
too much importance. And normally, I would take the position that a
property like this would have to appear in the declaration somewhere, to
capture the fact that conformance to these properties is not merely
accidental and subject to change tomorrow. But, lately I've been
questioning this.
Reasons to have some sort of indicator of "I am an initialization-free
class":
- Allows author to capture design intent
- Allows compiler to type-check conformance to requirements, based on
design intent
- Propagates design intent into documentation, where subclasses can
see that this is safe to extend
But it occurs to me that all of these are within the bounds of what
_annotations_ are allowed to do; we did the same with
`@FunctionalInterface` that, while not required, turns on additional
compile-time type checking and documentation.
I am currently thinking that we can adopt Dan's rules at the language
level, and have an annotation @InitializationFree, which: (a) declares
intent to conform to the contract of initialization-freedom, (b)
unleashes compile-time type checking for same, and (c) causes a blurb to
be emitted into the Javadoc along the lines of "This is an
initialization-free class" -- but that the language specification works
entirely in terms of structural properties. We can say that "inline
classes may extend initialization-free inline classes", and be done.
Regardless of compile-time type checking, we will likely want to do the
analysis and write the result into the classfile (e.g., an
InitializationFree class attribute), which can be seen as a
necessary-but-not-sufficient signal for the VM to allow the class to be
used as a super for an inline class. When we go to load a class with
this attribute, we can verify that it actually conforms:
- Fields: no fields
- Constructors/initializers: no ctor/initializer
- Class declaration: it is abstract, and its super is i-free
- Synchronization: No methods have ACC_SYCHRONIZED
When we go to load an inline class, we check the i-free bit on the
superclass.
None of these checks seem all that burdensome to the VM.
It does mean that dusty abstract classes need to be recompiled before
they can be bases for inline classes, which, on balance, seems OK.
> Language model
>
> An inline class may extend another class, as long as the superclass has the following properties:
> - It has no instance fields
> - It has no constructors
> - It has no instance initializers
> - It is abstract* or Object
> - It extends another class with these properties
>
> Subtype polymorphism works the same for superclasses as it does for superinterfaces.
>
> (*Remi points out that we could drop the 'abstract' restriction,
> meaning there may be identity instances of the superclass. Given the
> restriction on fields, though, I'm struggling to envision a use case;
> the consensus is that 'new Object()' is probably something we want to
> *stop* supporting.)
>
> Call a class that satisfies these constraints an "initialization-free class" (bikeshedding on this term is welcome!). Like an interface, its value set may include references to both inline class instances and identity class instances.
>
> We*do *not* want the initialization-free property to be expressed as a class modifier—this feature is too obscure to deserve that much prominence, encouraging every class author to consider one more degree of freedom; and we don't want every class to have to manually opt in.
>
> But we*do* need the initialization-free property to be part of the public information about the class. For example, the javadoc should say something like "this is an initialization-free class". Otherwise, it's impossible to tell the difference between, e.g., a class with no fields and a class with private fields.
>
> In the past, a Java class declaration that lacks a constructor always got a default constructor. In this model, however, an initialization-free class has no constructor at all. A 'super()' call directed at such a class is a no-op.
More information about the valhalla-spec-observers
mailing list