Statements before super() (Preview)

Changes to the Java® Language Specification • Version 21-internal-adhoc.gbierman.20230420

This document describes changes to the Java Language Specification to support Statements before super(), which is a proposed feature of Java SE 21. See JEP 447 for an overview of the feature.

Changes are described with respect to existing sections of the JLS. New text is indicated like this and deleted text is indicated like this. Explanation and discussion, as needed, is set aside in grey boxes.

2023-04-20: First draft

Chapter 6: Names

6.5 Determining the Meaning of a Name

6.5.6 Meaning of Expression Names

6.5.6.1 Simple Expression Names

If an expression name consists of a single Identifier, then there must be exactly one declaration denoting either a local variable, formal parameter, exception parameter, or field in scope at the point at which the identifier occurs. Otherwise, a compile-time error occurs.

If the declaration denotes an instance variable of a class C (8.3.1.1), then both of the following must be true, or a compile-time error occurs:

For example, the expression name must not appear in the body of a static method declared by C, nor in the body of an instance method of a static class nested within C.

If the declaration denotes a local variable, formal parameter, or exception parameter, let X be the innermost method declaration, constructor declaration, instance initializer, static initializer, field declaration, or explicit constructor invocation statement which encloses the local variable or parameter declaration. If the expression name appears directly or indirectly in the body of a local class, local interface, or anonymous class D declared directly in X, then both of the following must be true, or a compile-time error occurs:

For example, the expression name must not appear in the body of a static method declared by D, nor (if D is a local interface) in the body of a default method of D.

If the declaration denotes a local variable, formal parameter, or exception parameter that is neither final nor effectively final (4.12.4), it is a compile-time error if the expression name appears either in an inner class enclosed directly or indirectly by X, or in a lambda expression contained by X (15.27).

The net effect of these rules is that a local variable, formal parameter, or exception parameter can only be referenced from a nested class or interface declared within its scope if (i) the reference is not within a static context, (ii) there is a chain of inner (non-static) classes from the reference to the variable declaration, and (iii) the variable is final or effectively final. References from lambda expressions also require the variable to be final or effectively final.

If the declaration declares a final variable which is definitely assigned before the simple expression, the meaning of the name is the value of that variable. Otherwise, the meaning of the expression name is the variable declared by the declaration.

If the expression name appears in an assignment context, invocation context, or casting context, then the type of the expression name is the declared type of the field, local variable, or parameter after capture conversion (5.1.10).

Otherwise, the type of the expression name is the declared type of the field, local variable or parameter.

That is, if the expression name appears "on the right hand side", its type is subject to capture conversion. If the expression name is a variable that appears "on the left hand side", its type is not subject to capture conversion.

Example 6.5.6.1-1. Simple Expression Names

class Test {
    static int v;
    static final int f = 3;
    public static void main(String[] args) {
        int i;
        i = 1;
        v = 2;
        f = 33;  // compile-time error
        System.out.println(i + " " + v + " " + f);
    }
}

In this program, the names used as the left-hand-sides in the assignments to i, v, and f denote the local variable i, the field v, and the value of f (not the variable f, because f is a final variable). The example therefore produces an error at compile time because the last assignment does not have a variable as its left-hand side. If the erroneous assignment is removed, the modified code can be compiled and it will produce the output:

1 2 3

Example 6.5.6.1-2. References to Instance Variables

class Test {
    static String a;
    String b;

    String concat1() {
        return a + b;
    }

    static String concat2() {
        return a + b;  // compile-time error
    }

    int index() {
        interface I {
            class Matcher {
                void check() {
                    if (a == null ||
                        b == null) {  // compile-time error
                        throw new IllegalArgumentException();
                    }
                }
                int match(String s, String t) {
                    return s.indexOf(t);
                }
            }
    }

    I.Matcher matcher = new I.Matcher();
    matcher.check();
    return matcher.match(a, b);
    }
}

The fields a and b are in scope throughout the body of class Test. However, using the name b in the static context of the concat2 method, or in the declaration of the nested class Matcher that is not an inner class of Test, is illegal.

Example 6.5.6.1-3. References to Local Variables and Formal Parameters

class Test {
    public static void main(String[] args) {
        String first = args[0];

        class Checker {
            void checkWhitespace(int x) {
                String arg = args[x];
                if (!arg.trim().equals(arg)) {
                    throw new IllegalArgumentException();
                }
            }

            static void checkFlag(int x) {
                String arg = args[x];  // compile-time error
                if (!arg.startsWith("-")) {
                    throw new IllegalArgumentException();
                }
            }

            static void checkFirst() {
                 Runnable r = new Runnable() {
                     public void run() {
                         if (first == null) {  // compile-time error
                             throw new IllegalArgumentException();
                         }
                     }
         };
                 r.run();
             }
        }

        final Checker c = new Checker();
        c.checkFirst();
        for (int i = 1; i < args.length; i++) {
            Runnable r = () -> {
                c.checkWhitespace(i);  // compile-time error
                c.checkFlag(i);  // compile-time error
            };
        }
    }
}

The formal parameter args is in scope throughout the body of method main. args is effectively final, so the name args can be used in the instance method checkWhitespace of local class Checker. However, using the name args in the static context of the checkFlag method of local class Checker is illegal.

The local variable first is in scope for the remainder of the body of method main. first is also effectively final. However, the anonymous class declared in checkFirst is not an inner class of Checker, so using the name first in the anonymous class body is illegal. (A lambda expression in the body of checkFirst would similarly be unable to refer to first, because the lambda expression would occur in a static context.)

The local variable c is in scope for the last few lines of the body of method main, and is declared final, so the name c can be used in the body of the lambda expression.

The local variable i is in scope throughout the for loop. However, i is not effectively final, so using the name i in the body of the lambda expression is illegal.

6.5.7 Meaning of Method Names

6.5.7.1 Simple Method Names

A simple method name appears in the context of a method invocation expression (15.12). The simple method name consists of a single UnqualifiedMethodIdentifier which specifies the name of the method to be invoked. The rules of method invocation require that the UnqualifiedMethodIdentifier denotes a method that is in scope at the point of the method invocation. The rules also prohibit (15.12.3) a reference to an instance method occurring in a static context (8.1.3), a pre-initialization context of the associated instance (8.8.7.1), or in a nested class or interface other than an inner class of the class or interface which declares the instance method.

Example 6.5.7.1-1. Simple Method Names

The following program demonstrates the role of scoping when determining which method to invoke.

class Super {
    void f2(String s)       {}
    void f3(String s)       {}
    void f3(int i1, int i2) {}
}

class Test {
    void f1(int i) {}
    void f2(int i) {}
    void f3(int i) {}

    void m() {
        new Super() {
            {
                f1(0);  // OK, resolves to Test.f1(int)
                f2(0);  // compile-time error
                f3(0);  // compile-time error
            }
        };
    }
}

For the invocation f1(0), only one method named f1 is in scope. It is the method Test.f1(int), whose declaration is in scope throughout the body of Test including the anonymous class declaration. 15.12.1 chooses to search in class Test since the anonymous class declaration has no member named f1. Eventually, Test.f1(int) is resolved.

For the invocation f2(0), two methods named f2 are in scope. First, the declaration of the method Super.f2(String) is in scope throughout the anonymous class declaration. Second, the declaration of the method Test.f2(int) is in scope throughout the body of Test including the anonymous class declaration. (Note that neither declaration shadows the other, because at the point where each is declared, the other is not in scope.) 15.12.1 chooses to search in class Super because it has a member named f2. However, Super.f2(String) is not applicable to f2(0), so a compile-time error occurs. Note that class Test is not searched.

For the invocation f3(0), three methods named f3 are in scope. First and second, the declarations of the methods Super.f3(String) and Super.f3(int,int) are in scope throughout the anonymous class declaration. Third, the declaration of the method Test.f3(int) is in scope throughout the body of Test including the anonymous class declaration. 15.12.1 chooses to search in class Super because it has a member named f3. However, Super.f3(String) and Super.f3(int,int) are not applicable to f3(0), so a compile-time error occurs. Note that class Test is not searched.

Choosing to search a nested class's superclass hierarchy before the lexically enclosing scope is called the "comb rule" (15.12.1).

Chapter 8: Classes

8.1 Class Declarations

8.1.3 Inner Classes and Enclosing Instances

An inner class is a nested class that is not explicitly or implicitly static.

An inner class is one of the following:

The following nested classes are implicitly static, so are not inner classes:

All of the rules that apply to nested classes apply to inner classes. In particular, an inner class may declare and inherit static members (8.2), and declare static initializers (8.7), even though the inner class itself is not static.

There are no "inner interfaces" because every nested interface is implicitly static (9.1.1.3).

Example 8.1.3-1. Inner Class Declarations and Static Members

class HasStatic {
    static int j = 100;
}

class Outer {
    class Inner extends HasStatic {
        static {
            System.out.println("Hello from Outer.Inner");
        }

        static       int x = 3;
        static final int y = 4;

        static void hello() {
            System.out.println("Hello from Outer.Inner.hello");
        }

        static class VeryNestedButNotInner
            extends NestedButNotInner {}
    }

    static class NestedButNotInner {
        int z = Inner.x;
    }

    interface NeverInner {}  // Implicitly static, so never inner
}

Prior to Java SE 16, an inner class could not declare static initializers, and could only declare static members that were constant variables (4.12.4).

A construct (statement, local variable declaration statement, local class declaration, local interface declaration, or expression) occurs in a static context if the innermost:

which encloses the construct is one of the following:

Note that a construct which appears in a constructor declaration or an instance initializer does not occur in a static context.

The purpose of a static context is to demarcate code that must not refer explicitly or implicitly to the current instance of the class whose declaration lexically encloses the static context. The purpose of a static context is to demarcate code for which there is no current instance defined of the class whose declaration lexically encloses the static context. Consequently, code that occurs in a static context is restricted in the following ways:

An inner class C is a direct inner class of a class or interface O if O is the immediately enclosing class or interface declaration of C and the declaration of C does not occur in a static context.

If an inner class is a local class or an anonymous class, it may be declared in a static context, and in that case is not considered an inner class of any enclosing class or interface.

A class C is an inner class of class or interface O if it is either a direct inner class of O or an inner class of an inner class of O.

It is unusual, but possible, for the immediately enclosing class or interface declaration of an inner class to be an interface. This only occurs if the class is a local or anonymous class declared in a default or static method body (9.4).

A class or interface O is the zeroth lexically enclosing class or interface declaration of itself.

A class O is the n'th lexically enclosing class declaration of a class C if it is the immediately enclosing class declaration of the n-1'th lexically enclosing class declaration of C.

An instance i of a direct inner class C of a class or interface O is associated with an instance of O, known as the immediately enclosing instance of i. The immediately enclosing instance of an object, if any, is determined when the object is created (15.9.2).

An object o is the zeroth lexically enclosing instance of itself.

An object o is the n'th lexically enclosing instance of an instance i if it is the immediately enclosing instance of the n-1'th lexically enclosing instance of i.

An instance of an inner local class or an anonymous class whose declaration occurs in a static context has no immediately enclosing instance. Also, an instance of a static nested class (8.1.1.4) has no immediately enclosing instance.

For every superclass S of C which is itself a direct inner class of a class or interface SO, there is an instance of SO associated with i, known as the immediately enclosing instance of i with respect to S. The immediately enclosing instance of an object with respect to its class' direct superclass, if any, is determined when the superclass constructor is invoked via an explicit constructor invocation statement (8.8.7.1).

When an inner class (whose declaration does not occur in a static context) refers to an instance variable that is a member of a lexically enclosing class or interface declaration, the variable of the corresponding lexically enclosing instance is used.

Any local variable, formal parameter, or exception parameter used but not declared in an inner class must either be final or effectively final (4.12.4), as specified in 6.5.6.1.

Any local variable used but not declared in an inner class must be definitely assigned (16) before the body of the inner class, or a compile-time error occurs.

Similar rules on variable use apply in the body of a lambda expression (15.27.2).

A blank final field (4.12.4) of a lexically enclosing class or interface declaration may not be assigned within an inner class, or a compile-time error occurs.

Example 8.1.3-2. Inner Class Declarations

class Outer {
    int i = 100;
    static void classMethod() {
        final int l = 200;
        class LocalInStaticContext {
            int k = i;  // Compile-time error
            int m = l;  // OK
        }
    }
    void foo() {
        class Local {  // A local class
            int j = i;
        }
    }
}

The declaration of class LocalInStaticContext occurs in a static context due to being within the static method classMethod. Instance variables of class Outer are not available within the body of a static method. In particular, instance variables of Outer are not available inside the body of LocalInStaticContext. However, local variables from the surrounding method may be referred to without error (provided they are declared final or are effectively final).

Inner classes whose declarations do not occur in a static context may freely refer to the instance variables of their enclosing class declaration. An instance variable is always defined with respect to an instance. In the case of instance variables of an enclosing class declaration, the instance variable must be defined with respect to an enclosing instance of the inner class. For example, the class Local above has an enclosing instance of class Outer. As a further example:

class WithDeepNesting {
    boolean toBe;
    WithDeepNesting(boolean b) { toBe = b; }

    class Nested {
        boolean theQuestion;
        class DeeplyNested {
            DeeplyNested(){
                theQuestion = toBe || !toBe;
            }
        }
    }
}

Here, every instance of WithDeepNesting.Nested.DeeplyNested has an enclosing instance of class WithDeepNesting.Nested (its immediately enclosing instance) and an enclosing instance of class WithDeepNesting (its 2nd lexically enclosing instance).

8.8 Constructor Declarations

8.8.7 Constructor Body

The first statement of a constructor body may be an explicit invocation of another constructor of the same class or of the direct superclass (8.8.7.1).

A constructor body may contain an explicit invocation of another constructor of the same class or of the direct superclass (8.8.7.1).

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

It is a compile-time error for a constructor to directly or indirectly invoke itself through a series of one or more explicit constructor invocations involving this.

If a constructor body does not begin with contain an explicit constructor invocation and the constructor being declared is not part of the primordial class Object, then the constructor body implicitly begins with a superclass constructor invocation "super();", an invocation of the constructor of its direct superclass that takes no arguments.

Except for the possibility of explicit constructor invocations, and the prohibition on explicitly returning a value prohibitions on return statements (14.17), the body of a constructor is like the body of a method (8.4.7).

If a constructor body contains an explicit constructor invocation, the BlockStatements preceding the explicit constructor invocation are called the prologue of the constructor body. The BlockStatements in a constructor with no explicit constructor invocation and the BlockStatements following the explicit constructor invocation in a constructor with an explicit constructor invocation are called the main body of the constructor.

A return statement (14.17) may be used in the main body of a constructor if it does not include an expression. It is a compile-time error if a return statement appears in the prologue of a constructor body.

Example 8.8.7-1. Constructor Bodies

class Point {
    int x, y;
    Point(int x, int y) { this.x = x; this.y = y; }
}
class ColoredPoint extends Point {
    static final int WHITE = 0, BLACK = 1;
    int color;
    ColoredPoint(int x, int y) {
        this(x, y, WHITE);
    }
    ColoredPoint(int x, int y, int color) {
        super(x, y);
        this.color = color;
    }
}

Here, the first constructor of ColoredPoint invokes the second, providing an additional argument; the second constructor of ColoredPoint invokes the constructor of its superclass Point, passing along the coordinates.

8.8.7.1 Explicit Constructor Invocations
ExplicitConstructorInvocation:
[TypeArguments] this ( [ArgumentList] ) ;
[TypeArguments] super ( [ArgumentList] ) ;
ExpressionName . [TypeArguments] super ( [ArgumentList] ) ;
Primary . [TypeArguments] super ( [ArgumentList] ) ;

The following productions from 4.5.1 and 15.12 are shown here for convenience:

TypeArguments:
< TypeArgumentList >
ArgumentList:
Expression {, Expression}

Explicit constructor invocation statements are divided into two kinds:

An explicit constructor invocation statement introduces a static context (8.1.3), which limits the use of constructs that refer to the current object. Notably, the keywords this and super are prohibited in a static context (15.8.3, 15.11.2), as are unqualified references to instance variables, instance methods, and type parameters of lexically enclosing declarations (6.5.5.1, 6.5.6.1, 15.12.3).

An explicit constructor invocation statement introduces a pre-initialization context of the current object. The pre-initialization context includes the prologue of the constructor (8.8.7) and the explicit constructor invocation statement itself. Within a pre-initialization context, constructs that refer explicitly or implicitly to the current object are disallowed. These include this or super expressions referring to the current object, unqualified references to instance variables or instance methods of the current object, method references referring to instance methods of the current object, and instantiations of inner classes of the current object's class for which the current object is the enclosing instance (8.1.3).

If TypeArguments is present to the left of this or super, then it is a compile-time error if any of the type arguments are wildcards (4.5.1).

Let C be the class being instantiated, and let S be the direct superclass of C.

If a superclass constructor invocation statement is unqualified, then:

If a superclass constructor invocation statement is qualified, then:

The exception types that an explicit constructor invocation statement can throw are specified in 11.2.2.

Evaluation of an alternate constructor invocation statement proceeds by first evaluating the arguments to the constructor, left-to-right, as in an ordinary method invocation; and then invoking the constructor.

Evaluation of a superclass constructor invocation statement proceeds as follows:

  1. Let i be the instance being created. The immediately enclosing instance of i with respect to S (if any) must be determined:

    • 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.

    • Otherwise, if the superclass constructor invocation is unqualified, then S is necessarily an inner local class or an inner member class.

      If S is an inner local class, let O be the immediately enclosing class or interface declaration of S.

      If S is an inner member class, let O be the innermost enclosing class of C of which S is a member.

      Let n be an integer (n 1) such that O is the n'th lexically enclosing class or interface declaration of C.

      The immediately enclosing instance of i with respect to S is the n'th lexically enclosing instance of this.

      While it may be the case that S is a member of C due to inheritance, the zeroth lexically enclosing instance of this (that is, this itself) is never used as the immediately enclosing instance of i with respect to S.

    • Otherwise, if the superclass constructor invocation is qualified, then the Primary expression or the ExpressionName immediately preceding ".super", p, is evaluated.

      If p evaluates to null, a NullPointerException is raised, and the superclass constructor invocation completes abruptly.

      Otherwise, the result of this evaluation is the immediately enclosing instance of i with respect to S.

  2. After determining the immediately enclosing instance of i with respect to S (if any), evaluation of the superclass constructor invocation statement proceeds by evaluating the arguments to the constructor, left-to-right, as in an ordinary method invocation; and then invoking the constructor.

  3. Finally, if the superclass constructor invocation statement completes normally, then all instance variable initializers of C and all instance initializers of C are executed. If an instance initializer or instance variable initializer I textually precedes another instance initializer or instance variable initializer J, then I is executed before J.

    Execution of instance variable initializers and instance initializers is performed regardless of whether the superclass constructor invocation actually appears as an explicit constructor invocation statement or is provided implicitly. (An alternate constructor invocation does not perform this additional implicit execution.)

Example 8.8.7.1-1. Restrictions on Explicit Constructor Invocation Statements

If the first constructor of ColoredPoint in the example from 8.8.7 were changed as follows:

class Point {
    int x, y;
    Point(int x, int y) { this.x = x; this.y = y; }
}
class ColoredPoint extends Point {
    static final int WHITE = 0, BLACK = 1;
    int color;
    ColoredPoint(int x, int y) {
        this(x, y, color);  // Changed to color from WHITE
    }
    ColoredPoint(int x, int y, int color) {
        super(x, y);
        this.color = color;
    }
}

then a compile-time error would occur, because the instance variable color cannot be used by a explicit constructor invocation statement.

Example 8.8.7.1-2. Qualified Superclass Constructor Invocation

In the code below, ChildOfInner has no lexically enclosing class or interface declaration, so an instance of ChildOfInner has no enclosing instance. However, the superclass of ChildOfInner (Inner) has a lexically enclosing class declaration (Outer), and an instance of Inner must have an enclosing instance of Outer. The enclosing instance of Outer is set when an instance of Inner is created. Therefore, when we create an instance of ChildOfInner, which is implicitly an instance of Inner, we must provide the enclosing instance of Outer via a qualified superclass invocation statement in ChildOfInner's constructor. The instance of Outer is called the immediately enclosing instance of ChildOfInner with respect to Inner.

class Outer {
    class Inner {}
}
class ChildOfInner extends Outer.Inner {
    ChildOfInner() { (new Outer()).super(); }
}

Perhaps surprisingly, the same instance of Outer may serve as the immediately enclosing instance of ChildOfInner with respect to Inner for multiple instances of ChildOfInner. These instances of ChildOfInner are implicitly linked to the same instance of Outer. The program below achieves this by passing an instance of Outer to the constructor of ChildOfInner, which uses the instance in a qualified superclass constructor invocation statement. The rules for an explicit constructor invocation statement do not prohibit using formal parameters of the constructor that contains the statement.

class Outer {
    int secret = 5;
    class Inner {
        int  getSecret()      { return secret; }
        void setSecret(int s) { secret = s; }
    }
}
class ChildOfInner extends Outer.Inner {
    ChildOfInner(Outer x) { x.super(); }
}

public class Test {
    public static void main(String[] args) {
        Outer x = new Outer();
        ChildOfInner a = new ChildOfInner(x);
        ChildOfInner b = new ChildOfInner(x);
        System.out.println(b.getSecret());
        a.setSecret(6);
        System.out.println(b.getSecret());
    }
}

This program produces the output:

5
6

The effect is that manipulation of instance variables in the common instance of Outer is visible through references to different instances of ChildOfInner, even though such references are not aliases in the conventional sense.

Chapter 12: Execution

12.5 Creation of New Class Instances

A new class instance is explicitly created when evaluation of a class instance creation expression (15.9) causes a class to be instantiated.

A new class instance may be implicitly created in the following situations:

Each of these situations identifies a particular constructor (8.8) to be called with specified arguments (possibly none) as part of the class instance creation process.

Whenever a new class instance is created, memory space is allocated for it with room for all the instance variables declared in the class and all the instance variables declared in each superclass of the class, including all the instance variables that may be hidden (8.3).

If there is not sufficient space available to allocate memory for the object, then creation of the class instance completes abruptly with an OutOfMemoryError. Otherwise, all the instance variables in the new object, including those declared in superclasses, are initialized to their default values (4.12.5).

Just before a reference to the newly created object is returned as the result, the indicated constructor is processed to initialize the new object using the following procedure:

  1. Assign the arguments for the constructor to newly created parameter variables for this constructor invocation.

  2. If this constructor begins with an explicit constructor invocation (8.8.7.1) of another constructor in the same class (using this), then evaluate the arguments and process that constructor invocation recursively using these same five steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason; otherwise, continue with step 5. If this constructor contains an explicit constructor invocation (8.8.7.1), then execute the BlockStatements of the prologue of the constructor body. If execution of any statement completes abruptly, then execution of the constructor completes abruptly for the same reason; otherwise, continue with step 3.

  3. If this constructor contains an explicit constructor invocation (8.8.7.1) of another constructor in the same class (using this), then evaluate the arguments and process that constructor invocation recursively using these same six steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason; otherwise, continue with step 6.

  4. This constructor does not begin with contain an explicit constructor invocation of another constructor in the same class (using this). If this constructor is for a class other than Object, then this constructor will begin with contains an explicit or implicit invocation of a superclass constructor (using super). Evaluate the arguments and process that superclass constructor invocation recursively using these same five six steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, continue with step 4 5.

  5. 4. Execute the instance initializers and instance variable initializers for this class, assigning the values of instance variable initializers to the corresponding instance variables, in the left-to-right order in which they appear textually in the source code for the class. If execution of any of these initializers results in an exception, then no further initializers are processed and this procedure completes abruptly with that same exception. Otherwise, continue with step 5 6.

  6. 5. Execute the rest of the main body of this constructor. If that execution completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, this procedure completes normally.

Unlike C++, the Java programming language does not specify altered rules for method dispatch during the creation of a new class instance. If methods are invoked that are overridden in subclasses in the object being initialized, then these overriding methods are used, even before the new object is completely initialized.

Example 12.5-1. Evaluation of Instance Creation

class Point {
    int x, y;
    Point() { x = 1; y = 1; }
}
class ColoredPoint extends Point {
    int color = 0xFF00FF;
}
class Test {
    public static void main(String[] args) {
        ColoredPoint cp = new ColoredPoint();
        System.out.println(cp.color);
    }
}

Here, a new instance of ColoredPoint is created. First, space is allocated for the new ColoredPoint, to hold the fields x, y, and color. All these fields are then initialized to their default values (in this case, 0 for each field). Next, the ColoredPoint constructor with no arguments is first invoked. Since ColoredPoint declares no constructors, a default constructor of the following form is implicitly declared:

ColoredPoint() { super(); }

This constructor then invokes the Point constructor with no arguments. The Point constructor does not begin with an invocation of a constructor, so the Java compiler provides an implicit invocation of its superclass constructor of no arguments, as though it had been written:

Point() { super(); x = 1; y = 1; }

Therefore, the constructor for Object which takes no arguments is invoked.

The class Object has no superclass, so the recursion terminates here. Next, any instance initializers and instance variable initializers of Object are invoked. Next, the body of the constructor of Object that takes no arguments is executed. No such constructor is declared in Object, so the Java compiler supplies a default one, which in this special case is:

Object() { }

This constructor executes without effect and returns.

Next, all initializers for the instance variables of class Point are executed. As it happens, the declarations of x and y do not provide any initialization expressions, so no action is required for this step of the example. Then the body of the Point constructor is executed, setting x to 1 and y to 1.

Next, the initializers for the instance variables of class ColoredPoint are executed. This step assigns the value 0xFF00FF to color. Finally, the rest of the body of the ColoredPoint constructor is executed (the part after the invocation of super); there happen to be no statements in the rest of the body, so no further action is required and initialization is complete.

Example 12.5-2. Dynamic Dispatch During Instance Creation

class Super {
    Super() { printThree(); }
    void printThree() { System.out.println("three"); }
}
class Test extends Super {
    int three = (int)Math.PI;  // That is, 3
    void printThree() { System.out.println(three); }

    public static void main(String[] args) {
        Test t = new Test();
        t.printThree();
    }
}

This program produces the output:

0
3

This shows that the invocation of printThree in the constructor for class Super does not invoke the definition of printThree in class Super, but rather invokes the overriding definition of printThree in class Test. This method therefore runs before the field initializers of Test have been executed, which is why the first value output is 0, the default value to which the field three of Test is initialized. The later invocation of printThree in method main invokes the same definition of printThree, but by that point the initializer for instance variable three has been executed, and so the value 3 is printed.

Chapter 15: Expressions

15.8 Primary Expressions

15.8.3 this

The keyword this may be used as an expression in the following contexts:

When used as an expression, the keyword this denotes a value that is a reference either to the object for which the instance method was invoked (15.12), or to the object being constructed. The value denoted by this in a lambda body (15.27.2) is the same as the value denoted by this in the surrounding context.

The keyword this is also used in explicit constructor invocation statements (8.8.7.1), and to denote the receiver parameter of a method or constructor (8.4).

It is a compile-time error if a this expression occurs in a static context (8.1.3) or in a pre-initialization context of the associated instance (8.8.7.1).

Let C by the innermost enclosing class or interface declaration of a this expression. If C is generic, with type parameters F1,...,Fn, the type of this is C<F1,...,Fn>. Otherwise, the type of this is C.

At run time, the class of the actual object referred to may be C or a subclass of C (8.1.5.

Example 15.8.3-1. The this Expression

class IntVector {
    int[] v;
    boolean equals(IntVector other) {
        if (this == other)
            return true;
        if (v.length != other.v.length)
            return false;
        for (int i = 0; i < v.length; i++) {
            if (v[i] != other.v[i]) return false;
        }
        return true;
    }
}

Here, the class IntVector implements a method equals, which compares two vectors. If the other vector is the same vector object as the one for which the equals method was invoked, then the check can skip the length and value comparisons. The equals method implements this check by comparing the reference to the other object to this.

15.11 Field Access Expressions

15.11.2 Accessing Superclass Members using super

The form super.Identifier refers to the field named Identifier of the current object, but with the current object viewed as an instance of the superclass of the current class.

The form T.super.Identifier refers to the field named Identifier of the lexically enclosing instance corresponding to T, but with that instance viewed as an instance of the superclass of T.

The forms using the keyword super may be used in the locations within a class declaration that allow the keyword this as an expression (15.8.3).

It is a compile-time error if a field access expression using the keyword super appears in a static context (8.1.3) or in a pre-initialization context of the associated instance (8.8.7.1).

For a field access expression of the form super.Identifier:

For a field access expression of the form T.super.Identifier:

Suppose that a field access expression super.f appears within class C, and the immediate superclass of C is class S. If f in S is accessible from class C (6.6), then super.f is treated as if it had been the expression this.f in the body of class S. Otherwise, a compile-time error occurs.

Thus, super.f can access the field f that is accessible in class S, even if that field is hidden by a declaration of a field f in class C.

Suppose that a field access expression T.super.f appears within class C, and the immediate superclass of the class denoted by T is a class whose fully qualified name is S. If f in S is accessible from C, then T.super.f is treated as if it had been the expression this.f in the body of class S. Otherwise, a compile-time error occurs.

Thus, T.super.f can access the field f that is accessible in class S, even if that field is hidden by a declaration of a field f in class T.

Example 15.11.2-1. The super Expression

interface I           { int x = 0; }
class T1 implements I { int x = 1; }
class T2 extends T1   { int x = 2; }
class T3 extends T2 {
    int x = 3;
    void test() {
        System.out.println("x=\t\t"          + x);
        System.out.println("super.x=\t\t"    + super.x);
        System.out.println("((T2)this).x=\t" + ((T2)this).x);
        System.out.println("((T1)this).x=\t" + ((T1)this).x);
        System.out.println("((I)this).x=\t"  + ((I)this).x);
    }
}
class Test {
    public static void main(String[] args) {
        new T3().test();
    }
}

This program produces the output:

x=              3
super.x=        2
((T2)this).x=   2
((T1)this).x=   1
((I)this).x=    0

Within class T3, the expression super.x has the same effect as ((T2)this).x when x has package access. Note that super.x is not specified in terms of a cast, due to difficulties around access to protected members of the superclass.

15.12 Method Invocation Expressions

15.12.3 Compile-Time Step 3: Is the Chosen Method Appropriate?

If there is a most specific method declaration for a method invocation, it is called the compile-time declaration for the method invocation.

It is a compile-time error if an argument to a method invocation is not compatible with its target type, as derived from the invocation type of the compile-time declaration.

If the compile-time declaration is applicable by variable arity invocation, then where the last formal parameter type of the invocation type of the method is Fn[], it is a compile-time error if the type which is the erasure of Fn is not accessible (6.6) at the point of invocation.

If the compile-time declaration is void, then the method invocation must be a top level expression (that is, the Expression in an expression statement or in the ForInit or ForUpdate part of a for statement), or a compile-time error occurs. Such a method invocation produces no value and so must be used only in a situation where a value is not needed.

In addition, whether the compile-time declaration is appropriate may depend on the form of the method invocation expression before the left parenthesis, as follows:

The compile-time parameter types and compile-time result are determined as follows:

A method is signature polymorphic if all of the following are true:

The following compile-time information is then associated with the method invocation for use at run time:

If the result of the invocation type of the compile-time declaration is not void, then the type of the method invocation expression is obtained by applying capture conversion (5.1.10) to the return type of the invocation type of the compile-time declaration.

15.13 Method Reference Expressions

A method reference expression is used to refer to the invocation of a method without actually performing the invocation. Certain forms of method reference expression also allow class instance creation (15.9) or array creation (15.10) to be treated as if it were a method invocation.

MethodReference:
ExpressionName :: [TypeArguments] Identifier
Primary :: [TypeArguments] Identifier
ReferenceType :: [TypeArguments] Identifier
super :: [TypeArguments] Identifier
TypeName . super :: [TypeArguments] Identifier
ClassType :: [TypeArguments] new
ArrayType :: new

If TypeArguments is present to the right of ::, then it is a compile-time error if any of the type arguments are wildcards (4.5.1).

If a method reference expression has the form ExpressionName :: [TypeArguments] Identifier or Primary :: [TypeArguments] Identifier, it is a compile-time error if the type of the ExpressionName or Primary is not a reference type.

If a method reference expression has the form super :: [TypeArguments] Identifier, let E be the class or interface declaration immediately enclosing the method reference expression. It is a compile-time error if E is the class Object or if E is an interface.

If a method reference expression has the form TypeName . super :: [TypeArguments] Identifier, then:

If a method reference expression has the form super :: [TypeArguments] Identifier or TypeName . super :: [TypeArguments] Identifier, it is a compile-time error if the expression occurs in a static context (8.1.3) or in a pre-initialization context of the associated instance (8.8.7.1).

If a method reference expression has the form ClassType :: [TypeArguments] new, then:

If a method reference expression has the form ArrayType :: new, then ArrayType must denote a type that is reifiable (4.7), or a compile-time error occurs.

The target reference of an instance method (15.12.4.1) may be provided by the method reference expression using an ExpressionName, a Primary, or super, or it may be provided later when the method is invoked. The immediately enclosing instance of a new inner class instance (15.9.2) is provided by a lexically enclosing instance of this (8.1.3).

When more than one member method of a type has the same name, or when a class has more than one constructor, the appropriate method or constructor is selected based on the functional interface type targeted by the method reference expression, as specified in 15.13.1.

If a method or constructor is generic, the appropriate type arguments may either be inferred or provided explicitly. Similarly, the type arguments of a generic type mentioned by the method reference expression may be provided explicitly or inferred.

Method reference expressions are always poly expressions (15.2).

It is a compile-time error if a method reference expression occurs in a program in someplace other than an assignment context (5.2), an invocation context (5.3), or a casting context (5.5).

Evaluation of a method reference expression produces an instance of a functional interface type (9.8). This does not cause the execution of the corresponding method; instead, the execution may occur at a later time when an appropriate method of the functional interface is invoked.

Here are some method reference expressions, first with no target reference and then with a target reference:

String::length             // instance method
System::currentTimeMillis  // static method
List<String>::size  // explicit type arguments for generic type
List::size          // inferred type arguments for generic type
int[]::clone
T::tvarMember

System.out::println
"abc"::length
foo[x]::bar
(test ? list.replaceAll(String::trim) : list) :: iterator
super::toString

Here are some more method reference expressions:

String::valueOf       // overload resolution needed
Arrays::sort          // type arguments inferred from context
Arrays::<String>sort  // explicit type arguments

Here are some method reference expressions that represent a deferred creation of an object or an array:

ArrayList<String>::new     // constructor for parameterized type
ArrayList::new             // inferred type arguments
                           // for generic class
Foo::<Integer>new          // explicit type arguments
                           // for generic constructor
Bar<String>::<Integer>new  // generic class, generic constructor
Outer.Inner::new           // inner class constructor
int[]::new                 // array creation

It is not possible to specify a particular signature to be matched, for example, Arrays::sort(int[]). Instead, the functional interface provides argument types that are used as input to the overload resolution algorithm (15.12.2). This should satisfy the vast majority of use cases; when the rare need arises for more precise control, a lambda expression can be used.

The use of type argument syntax in the class name before a delimiter (List<String>::size) raises the parsing problem of distinguishing between < as a type argument bracket and < as a less-than operator. In theory, this is no worse than allowing type arguments in cast expressions; however, the difference is that the cast case only comes up when a ( token is encountered; with the addition of method reference expressions, the start of every expression is potentially a parameterized type.

15.13.1 Compile-Time Declaration of a Method Reference

The compile-time declaration of a method reference expression is the method to which the expression refers. In special cases, the compile-time declaration does not actually exist, but is a notional method that represents a class instance creation or an array creation. The choice of compile-time declaration depends on a function type targeted by the expression, just as the compile-time declaration of a method invocation depends on the invocation's arguments (15.12.3).

The search for a compile-time declaration mirrors the process for method invocations in 15.12.1 and 15.12.2, as follows:

It is a compile-time error if a method reference expression has the form ReferenceType :: [TypeArguments] Identifier, and the compile-time declaration is static, and ReferenceType is not a simple or qualified name (6.2).

It is a compile-time error if the method reference expression has the form super :: [TypeArguments] Identifier or TypeName . super :: [TypeArguments] Identifier, and the compile-time declaration is abstract.

It is a compile-time error if the method reference expression has the form super :: [TypeArguments] Identifier or TypeName . super :: [TypeArguments] Identifier, and the method reference expression occurs in a static context (8.1.3) or in a pre-initialization context of the associated instance (8.8.7.1).

It is a compile-time error if the method reference expression has the form TypeName . super :: [TypeArguments] Identifier, and TypeName denotes a class C, and the immediately enclosing class or interface declaration of the method reference expression is not C or an inner class of C.

It is a compile-time error if the method reference expression has the form TypeName . super :: [TypeArguments] Identifier, and TypeName denotes an interface, and there exists a method, distinct from the compile-time declaration, that overrides the compile-time declaration from a direct superclass or direct superinterface of the class or interface whose declaration immediately encloses the method reference expression (8.4.8, 9.4.1).

It is a compile-time error if the method reference expression is of the form ClassType :: [TypeArguments] new and a compile-time error would occur when determining an enclosing instance for ClassType as specified in 15.9.2 (treating the method reference expression as if it were an unqualified class instance creation expression).

A method reference expression of the form ReferenceType :: [TypeArguments] Identifier can be interpreted in different ways. If Identifier refers to an instance method, then the implicit lambda expression has an extra parameter compared to if Identifier refers to a static method. It is possible for ReferenceType to have both kinds of applicable methods, so the search algorithm described above identifies them separately, since there are different parameter types for each case.

An example of ambiguity is:

interface Fun<T,R> { R apply(T arg); }

class C {
    int size() { return 0; }
    static int size(Object arg) { return 0; }

    void test() {
        Fun<C, Integer> f1 = C::size;
          // Error: instance method size()
          // or static method size(Object)?
    }
}

This ambiguity cannot be resolved by providing an applicable instance method which is more specific than an applicable static method:

interface Fun<T,R> { R apply(T arg); }

class C {
    int size() { return 0; }
    static int size(Object arg) { return 0; }
    int size(C arg) { return 0; }

    void test() {
        Fun<C, Integer> f1 = C::size;
          // Error: instance method size()
          // or static method size(Object)?
    }
}

The search is smart enough to ignore ambiguities in which all the applicable methods (from both searches) are instance methods:

interface Fun<T,R> { R apply(T arg); }

class C {
    int size() { return 0; }
    int size(Object arg) { return 0; }
    int size(C arg) { return 0; }

    void test() {
        Fun<C, Integer> f1 = C::size;
          // OK: reference is to instance method size()
    }
}

For convenience, when the name of a generic type is used to refer to an instance method (where the receiver becomes the first parameter), the target type is used to determine the type arguments. This facilitates usage like Pair::first in place of Pair<String,Integer>::first. Similarly, a method reference like Pair::new is treated like a "diamond" instance creation (new Pair<>()). Because the "diamond" is implicit, this form does not instantiate a raw type; in fact, there is no way to express a reference to the constructor of a raw type.

For some method reference expressions, there is only one possible compile-time declaration with only one possible invocation type (15.12.2.6), regardless of the targeted function type. Such method reference expressions are said to be exact. A method reference expression that is not exact is said to be inexact.

A method reference expression ending with Identifier is exact if it satisfies all of the following:

A method reference expression of the form ClassType :: [TypeArguments] new is exact if it satisfies all of the following:

A method reference expression of the form ArrayType :: new is always exact.