Summary
The rules for when a member inner class can be created should be consolidated to take into account early construction contexts, introduced as part of JEP 482. Moreover, the language should reject local class creation expression where the local variable capture variables cannot be accessed from the site of the creation expression.
Problem
JEP 482 introduced the notion of early constructor context:
An expression occurs in the early construction context of a class C if it is contained in either the prologue of a constructor body of C, or it is nested in the explicit constructor invocation of a constructor body of C.
Unfortunately, the rules for selecting an enclosing instance when checking inner class creation expression and explicit constructor calls (where the superclass is a member inner class), defined in 15.9.2 and 8.8.7.1, do not take that into account.
As a result it is possible for the compiler to select an enclosing instance that is in an early construction context, which leads to unverifiable code.
Relatedly, the rules for validating local class creation expression and and explicit constructor calls (where the superclass is a local or anonymous class) fail to take into account a case where the local class can be referenced, but its captured variables cannot. This can happen when the local class creation occurs in a static context that is nested inside the local class - a consequence of this enhancement.
Solution
The proposed solution is twofold:
- for member inner class creation, or explicit constructor invocation involving member inner classes, we need to make sure that the selected enclosing instance cannot refer to a
this
under construction. That is, if the enclosing instance is inferred to beT.this
, then the expression must not occur in an early construction context for T, or a compile-time error occurs. - for local/anonymous inner class creation, or explicit constructor invocation involving local/anonymous classes, we need to make sure that the expression occurs in a context that can access any captured variable that is available to the local/anonymous class, or a compile-time error occurs.
Specification
Below are some temptative JLS changes. The JLS changes will likely be finalized in a follow up CSR associated with the third preview of the Flexible Constructor Bodies feature. New text is in bold.
Member inner class changes
15.9.2. Determining Enclosing Instances
If S is an inner member class, then ... let O be the innermost enclosing class declaration of which S is a member, and let U be the immediately enclosing class or interface declaration of the class instance creation expression. If the instance creation expression occurs in an early construction context of the class O, then a compile-time error occurs.
8.8.7.1. Explicit Constructor Invocations
If S is an inner member class, let O be the innermost enclosing class of C of which S is a member. If the superclass constructor invocation occurs in an early construction context of the class O, then a compile-time error occurs.
Local/anonymous class changes
15.9.2. Determining Enclosing Instances
If C is an anonymous class, then ... If the class instance creation expression occurs in a static context, then i has no immediately enclosing instance. If C is declared in the scope of a formal parameter f (resp. in the scope of a local variable l), it is a compile-time error if the class instance creation expression is not in the scope of f (resp. i).
If C is an inner local class, then ... If C occurs in a static context, then i has no immediately enclosing instance. If C is declared in the scope of a formal parameter f (resp. in the scope of a local variable l), it is a compile-time error if the class instance creation expression is not in the scope of f (resp. i).
8.8.7.1. Explicit Constructor Invocations
If S is not an inner class, or if the declaration of S occurs in a static context, then no immediately enclosing instance of i with respect to S exists. If S is a local or anonymous class, declared in the scope of a formal parameter f (resp. in the scope of a local variable l), it is a compile-time error if the class instance creation expression is not in the scope of f (resp. i).