3 Statements Before super(…)

In constructors, you may add statements that don't reference the instance being created before an explicit constructor invocation.

Note:

This is a preview feature. A preview feature is a feature whose design, specification, and implementation are complete, but is not permanent. A preview feature may exist in a different form or not at all in future Java SE releases. To compile and run code that contains preview features, you must specify additional command-line options. See Preview Language and VM Features.

For background information about statements before super(...), see JEP 447.

You can use this feature to prepare arguments for a superclass constructor by performing nontrivial computations or 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.

The Preconstruction Context of a Constructor

The preconstruction 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 preconstruction context of PositiveBigInteger consists of the argument value, and the if-statement that checks whether value is positive.

Code in a preconstruction context may not access the instance under construction. This means you can’t have the following in a preconstruction context:

  • Any unqualified this expression: Note that you don't need to use the this keyword 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 the super keyword 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();
        }
    }

Nested 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 preconstruction 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 preconstruction context. In the following example, both accessing F's member variable f and method hello() in the preconstruction 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.