Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8343143

Compiler Implementation for Flexible Constructor Bodies (Third Preview)

XMLWordPrintable

    • Icon: CSR CSR
    • Resolution: Approved
    • Icon: P4 P4
    • 24
    • tools
    • None
    • source
    • minimal
    • Language construct
    • SE

      Summary

      In constructors in the Java programming language, allow statements that do not read the fields of the instance being created to appear before an explicit constructor invocation.

      Problem

      The Java language disallows any statements in constructors prior to an explicit constructor invocation 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
      • Initialization of superclass fields by a subclass

      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 an explicit constructor invocation as long as two criteria are met:

      • The code does not refer to the current this instance (either explicitly or implicitly), except in a field access that appears on the left-hand side of a simple assignment.

      • There are no return statements

      Note that the first item is not new; except for the carve-out for simple assignments, it is exactly the same criterion that applies today to subexpressions of an explicit constructor invocation.

      Solution

      The grammar of a constructor body is changed from:

      ConstructorBody:
          { [ExplicitConstructorInvocation] [BlockStatements] } ;

      to:

      ConstructorBody:
          { [BlockStatements] } ;
          { [BlockStatements] ExplicitConstructorInvocation [BlockStatements] } ;

      The Java Language Specification classifies code that appears in the argument list of an explicit constructor invocation as being in a static context. This means that the arguments to the explicit constructor invocation are treated as if they were in a static method; in other words, as if no instance is available. The technical restrictions of a static context are stronger than necessary, however, and they prevent code that is useful and safe from appearing as constructor arguments. Moreover, the javac compiler has enforced more relaxed conditions, which have been exploited by many developers.

      For example, subexpressions of an explicit constructor invocation in a constructor body of an inner class commonly refer to the outer instance; for example:

      public class MyClass {
          public void doSomething() {
              // ...
          }
          public class MyThread extends Thread {
              public MyThread() {
                  super(MyClass.this::doSomething);
              }
          }
      }

      To fix the JLS, rather than revise the concept of a static context, we define a new, strictly weaker concept of an early construction context to cover both the arguments to an explicit constructor invocation and any statements that appear before it. The rules for code in an early construction context are similar to the rules for code in an instance method, except for one restriction: in an early construction context, code must not read the fields of the instance under construction.

      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. Two other options that were considered were:

      1. Allow multiple explicit constructor invocations, and use DA/DU analysis to ensure exactly one call is ever executed
      2. Allow explicit constructor invocations within try blocks, with the requirement that if any exceptions are caught the constructor must complete abruptly.

      Aside: The first condition would not require any change to the JVMS, whereas the second would require a (straightforward) change.

      It should also be noted that this feature does not change the semantics of any existing code.

      History

      This third preview of the feature is unchanged from the second preview to gain more experience and feedback. A number of corner-cases in the compiler implementation and specification have been corrected.

      Specification

      The JLS changes are identical to those in the second preview apart from minor bugs that have been noticed during the preview period.

      A spec is attached and is available at:

      https://cr.openjdk.org/~gbierman/jep492/jep492-20241101/specs/flexible-constructor-bodies-jls.html

            gbierman Gavin Bierman
            gbierman Gavin Bierman
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved: