Summary
Allow statements to appear in constructors before this()
or super()
call.
Problem
The Java language disallows statements in constructors prior to this()
/super()
as a way of preventing access to the current instance prior to superclass construction. This ensures that object construction proceeds in an orderly fashion "from the top down".
However, this rule prevents a variety of common patterns that are available to regular methods. For example:
- "Fail fast" validation of constructor parameters as the first order of business
- Creating an object to pass to the superclass constructor in two different parameter positions
- Complex preparation and/or selection of superclass constructor parameters
This rule is more restrictive than it needs to be. All of the existing semantic and safety guarantees relating to constructors are still preserved if code can appear prior to this()
/super()
as long as two criteria are met:
- The code does not refer to the current
this
instance (either explicitly or implicitly) - There are no
return
statements
Note that the first item is not new; it is exactly the same criterion that applies today to expressions within a this()
/super()
call.
Solution
Change the grammar for constructors containing an explicit this()
/super()
call from:
ConstructorBody:
{ [ExplicitConstructorInvocation] [BlockStatements] } ;
to:
ConstructorBody:
{ [BlockStatements] } ;
{ [BlockStatements] ExplicitConstructorInvocation [BlockStatements] } ;
Also, correct an error in the JLS, which currently defines the inside of a this()
/super()
call as a static context. This is inconsistent with long-standing practice by both developers and the compiler.
For example, code inside inner class constructor this()
/super()
statements commonly refer to the outer instance, as in this example:
public class MyClass {
public void doSomething() {
// ...
}
public class MyThread extends Thread {
public MyThread() {
super(MyClass.this::doSomething);
}
}
}
Instead, we redefine the inside of a this()
/super()
call and any prior statements as a pre-construction context, and clarify the distinction:
- A static context is one in which there is no
this
instance defined, so (for example) references tothis
, generic type parameters or outer instances make no sense. - A pre-construction context is one in which there is a
this
instance but it may not be referenced because it is uninitialized.
On amber-dev there was a good deal of discussion regarding the how far to expand the rules. The eventual consensus was to take a conservative approach, so the above set of changes represents a minimal choice. The further options that were considered are:
- Allow multiple
this()
/super()
calls, and use DA/DU analysis to ensure exactly one call is ever executed - Allow assignments to instance fields prior to
this()
/super()
to facilitate working around "this
escapes" - Allow
this()
/super()
withintry { }
blocks, with the requirement that if any exceptions are caught the constructor must complete abruptly.
As a side note, all but the last of these options are compatible with the existing JVMS; the last one would require a (straightforward) change.
It should also be noted that there is no change to the semantics of existing programs.
Specification
Summary of JLS modifications:
- Update the grammar to allow statements (other than
return
) to appear prior tosuper()
orthis()
. - Define the statements up to and including a
super()
orthis()
call as a "pre-construction context". - Narrow the definition of "static context" to exclude pre-construction contexts.
- Update restrictions on static contexts to also restrict pre-construction contexts where appropriate.
There are no explicit specification changes for record and enum classes; constructors in these classes inherit the new rules in the natural way:
- Enum constructors and non-canonical record constructors may invoke
this()
but notsuper()
; as a result, these constructors may now contain statements beforethis()
. - Canonical record constructors are not allowed to explicitly either invoke
super()
orthis()
, so there is no effect on them.
See the attached ZIP file for precise details.
Notes
Discussion on amber-dev (these are all one thread):
- https://mail.openjdk.org/pipermail/amber-dev/2023-January/007680.html
- https://mail.openjdk.org/pipermail/amber-dev/2022-October/007537.html
- https://mail.openjdk.org/pipermail/amber-dev/2022-November/007540.html
- https://mail.openjdk.org/pipermail/amber-dev/2022-December/007627.html
Discussion on compiler-dev:
There is also a prototype implementation in the Amber repository.
- csr of
-
JDK-8194743 Compiler implementation for Statements before super()
- Resolved
- relates to
-
JDK-8300786 JEP 447: Statements before super(...) (Preview)
- Closed