5 Flexible Constructor Bodies
In a constructor's body, you may add statements that don't reference the instance being created before an explicit constructor invocation.
For more information about flexible constructor bodies, see JEP 513.
You can use this feature to prepare arguments for a superclass constructor
            by performing nontrivial computations or to validate arguments you want to pass to a
            superclass constructor. The following example validates whether the argument
                value is positive before passing it to the superclass
            constructor:
               
public class PositiveBigInteger extends BigInteger {
    public PositiveBigInteger(long value) {
        if (value <= 0)
            throw new IllegalArgumentException("non-positive value");
        super(Long.toString(value));
    }
}The prologue of the constructor's body consists of the statements that appear
            before the super(...) invocation. The epilogue of the
            constructor's body consists of the statements that follow the
                super(...) invocation.
               
Early Construction Context
The early construction context of a constructor consists of the
        arguments to an explicit constructor invocation, such as super(...), and
        any statements before it.
                  
In the previous example, the early construction context of
                PositiveBigInteger consists of the argument
                Long.toString(value) and the if-statement that
            checks whether value is positive.
                  
Code in an early construction context may not access the instance under construction. This means you can’t have the following in an early construction context:
- 
                        
                        Any unqualified thisexpression: Note that you don't need to use thethiskeyword to access the instance under construction. For example:class A { int i; A() { // Error: Cannot reference this before supertype constructor has been // called this.i++; // Error: cannot reference i before supertype constructor has been // called i++; // Error: cannot reference this before supertype constructor has been // called this.hashCode(); // Error: cannot reference hashCode() before supertype constructor has // been called hashCode(); // Error: cannot reference this before supertype constructor has been // called System.out.print(this); super(); } }
- 
                        
                        Any field access, method invocation, or method reference qualified by super: Again, note that you don't need to use thesuperkeyword to access the superclass of the instance under construction:class D { int j; } class E extends D { E() { // Error: cannot reference super before supertype constructor has been called super.j++; // Error: cannot reference j before supertype constructor has been // called j++; super(); } }
Initializing Fields Before the super(...) Invocation
As mentioned previously, you can't read any of the fields of the current instance,
                whether declared in the same class as the constructor or in a superclass, until
                after the explicit constructor invocation. However, you can initialize fields of the
                current instance with the assignment operator before the super(...)
                invocation.
                     
Consider the following example, which consists of two classes:
                    Super and Sub, which extends
                    Super and overrides
                Super::overridenMethod:
                     
class Super {
    Super() { overriddenMethod(); }
    void overriddenMethod() { System.out.println("hello"); }
}
class Sub extends Super {
    final int x;
    
    Sub(int x) {
        // The Super constructor is implicitly invoked,
        // which calls overriddenMethod(), before initializing
        // the field x in Sub.
        this.x = x;
    }
    @Override
    void overriddenMethod() { System.out.println(x); }
    
    public static void main(String[] args) {
        Sub myApp = new Sub(42);
        myApp.overriddenMethod();
    }
}The example prints the following output:
0
42When the example invokes the constructor for Sub, it
                implicitly invokes the constructor for Super before assigning a
                value to the field x in Sub. As a result, when the
                example invokes Sub:overriddenMethod in the constructor for
                    Sub, it prints the uninitialized value of x,
                which is 0.
                     
You can initialize the field x in Sub
                and then invoke super() afterward:
                     
class BetterSub extends Super {
    final int x;
    
    BetterSub(int x) {        
        // Initialize the int x field in BetterSub before
        // invoking the Super constructor with super().
        this.x = x;
        super();
    }
    @Override
    void overriddenMethod() { System.out.println(x); }
    
    public static void main(String[] args) {
        BetterSub myApp = new BetterSub(42);
        myApp.overriddenMethod();
    }
}This example prints the following output:
42
42Nested Classes
A nested class is a member of its enclosing class, which means you can't access a nested class from its enclosing class's early construction context. For example:
class B {
    
    class C { }
    
    B() {
        // Error: cannot reference this before supertype constructor has been
        //        called
        new C();
        super();
    }
}However, a nested class's enclosing class is not one of its
                members, which means you can access its enclosing class from its early construction
                context. In the following example, both accessing F's member
                variable f and method hello() in the early
                construction context of its nested class G is permitted:
                     
class F {
    
    int f;
    
    void hello() {
        System.out.println("Hello!");
    }
    
    class G {
        G() {
            F.this.f++;
            hello();
            super();
        }
    }
}Records
Record constructors may not invoke super(...). However, noncanonical
                constructors must involve a canonical constructor by invoking
                    this(...). Statements may appear before
                    this(...).
                     
Remember that a canonical constructor is a constructor whose signature is the same as the record's component list. It initializes all the component fields of the record class. Alternative or noncanonical record constructors have argument lists that don't match the record's type parameters.
In the following example, the record RectanglePair contains a
                noncanonical constructor, RectanglePair(Pair<Float> corner).
                Because it's a noncanonical constructor, it must invoke a canonical constructor with
                    this(...). It contains several statements before
                    this(...) that retrieve both values from the
                    Pair<Float> parameter and validate that these values aren't
                negative:
                     
record Pair<T extends Number>(T x, T y) { }
record RectanglePair(float length, float width) {
    public RectanglePair(Pair<Float> corner) {
        float x = corner.x().floatValue();
        float y = corner.y().floatValue();
        if (x < 0 || y < 0) {
            throw new IllegalArgumentException("non-positive value");
        }
        this(corner.x().floatValue(), corner.y().floatValue());
    }
}See Alternative Record Constructors in Record Classes for more information.