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

Permit additional statements before this/super in constructors

XMLWordPrintable

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

      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 to this, 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() within try { } 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 to super() or this().
      • Define the statements up to and including a super() or this() 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 not super(); as a result, these constructors may now contain statements before this().
      • Canonical record constructors are not allowed to explicitly either invoke super() or this(), 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):

      Discussion on compiler-dev:

      There is also a prototype implementation in the Amber repository.

            gbierman Gavin Bierman
            vromero Vicente Arturo Romero Zaldivar
            Vicente Arturo Romero Zaldivar
            Votes:
            1 Vote for this issue
            Watchers:
            6 Start watching this issue

              Created:
              Updated:
              Resolved: