Flexible Constructor Bodies (Second Preview)

Changes to the Java® Language Specification • Version 24-loom+10-110

This document describes changes to the Java Language Specification to support Flexible Constructor Bodies, which is a proposed feature of Java SE 23. See JEP 482 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.

Changelog:

2024-06-04: Minor edits.

2024-05-14: First draft. Changes since the first preview:

  1. Change of title from Statements before super(...) to Flexible Constructor Bodies

  2. Rename "pre-construction context" to "early construction context"

  3. Allow a constructor body to assign to fields in the same class before making an explicit constructor invocation. This enables a constructor in a subclass to ensure that a constructor in a superclass never executes code which sees the default value of a field in the subclass (0, false, null). This can occur when, due to overriding, the superclass constructor invokes a method in the subclass that uses the field (see Example 12.5-3).

  4. Added missing treatment of definite assignment and constructor bodies with explicit constructor invocations.

  5. Simplified treatment of (inner) local and anonymous classes in early construction contexts. They now are always considered to have an immediately enclosing instance. What can be referenced from inside these local and anonymous classes is given by rules on what can be referenced from the early construction context of a particular class. This change also addresses pre-existing inadequacies in the specification of valid references that occur inside local and anonymous classes declared in the argument of an explicit constructor invocation.

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 all 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), or in a nested class or interface other than an inner class of the class or interface which declares the instance method. The rules (15.12.3) also prohibit a reference to an instance method occurring in any one of the following:

  1. a static context (8.1.3),

  2. a nested class or interface other than an inner class of the innermost class or interface of which the instance method is a member, or

  3. an early construction context (8.8.7.1) of a class where the instance method is a member.

Editorial: The struck-out sentence above offered an incorrect summary of the rules appearing in (15.12.3). Whilst it correctly asserted that the following is prohibited:

class T {
    void foo() { ... }
    static class Mid {
        class L {
            ... foo() ... // Compile-time error as L is not an inner class of T
        }
    }
}

It suggested that the following code is not allowed by the rules in (15.12.3), although it is.

class B {
    void foo() { ... }
}
class T extends B {
    class Mid {
        class L {
            ... foo() ... // No compile-time error even though L is not an inner class of B
        }
    }
 }

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

A class declaration defines a new class and describes how it is implemented (8.1).

A top level class (7.6) is a class declared directly in a compilation unit.

A nested class is any class whose declaration occurs within the body of another class or interface declaration. A nested class may be a member class (8.5, 9.5), a local class (14.3), or an anonymous class (15.9.5).

Some kinds of nested class are an inner class (8.1.3), which is a class that can refer to enclosing class instances, local variables, and type variables.

A nested class may be inner (8.1.3), in which case it may (depending on precisely where its declaration occurs) be able to refer to enclosing class instances, local variables, and type variables.

An enum class (8.9) is a class declared with abbreviated syntax that defines a small set of named class instances.

A record class (8.10) is a class declared with abbreviated syntax that defines a simple aggregate of values.

This chapter discusses the common semantics of all classes. Details that are specific to particular kinds of classes are discussed in the sections dedicated to these constructs.

A class may be declared public (8.1.1) so it can be referred to from code in any package of its module and potentially from code in other modules.

A class may be declared abstract (8.1.1.1), and must be declared abstract if it is incompletely implemented; such a class cannot be instantiated, but can be extended by subclasses. The degree to which a class can be extended can be controlled explicitly (8.1.1.2): it may be declared sealed to limit its subclasses, or it may be declared final to ensure no subclasses. Each class except Object is an extension of (that is, a subclass of) a single existing class (8.1.4) and may implement interfaces (8.1.5).

A class may be generic (8.1.2), that is, its declaration may introduce type variables whose bindings differ among different instances of the class.

Class declarations may be decorated with annotations (9.7) just like any other kind of declaration.

The body of a class declares members (fields, methods, classes, and interfaces), instance and static initializers, and constructors (8.1.7). The scope (6.3) of a member (8.2) is the entire body of the declaration of the class to which the member belongs. Field, method, member class, member interface, and constructor declarations may include the access modifiers public, protected, or private (6.6). The members of a class include both declared and inherited members (8.2). Newly declared fields can hide fields declared in a superclass or superinterface. Newly declared member classes and member interfaces can hide member classes and member interfaces declared in a superclass or superinterface. Newly declared methods can hide, implement, or override methods declared in a superclass or superinterface.

Field declarations (8.3) describe class variables, which are incarnated once, and instance variables, which are freshly incarnated for each instance of the class. A field may be declared final (8.3.1.2), in which case it can be assigned to only once. Any field declaration may include an initializer.

Member class declarations (8.5) describe nested classes that are members of the surrounding class. Member classes may be static, in which case they have no access to the instance variables of the surrounding class; or they may be inner classes.

Member interface declarations (8.5) describe nested interfaces that are members of the surrounding class.

Method declarations (8.4) describe code that may be invoked by method invocation expressions (15.12). A class method is invoked relative to the class; an instance method is invoked with respect to some particular object that is an instance of a class. A method whose declaration does not indicate how it is implemented must be declared abstract. A method may be declared final (8.4.3.3), in which case it cannot be hidden or overridden. A method may be implemented by platform-dependent native code (8.4.3.4). A synchronized method (8.4.3.6) automatically locks an object before executing its body and automatically unlocks the object on return, as if by use of a synchronized statement (14.19), thus allowing its activities to be synchronized with those of other threads (17).

Method names may be overloaded (8.4.9).

Instance initializers (8.6) are blocks of executable code that may be used to help initialize an instance when it is created (15.9).

Static initializers (8.7) are blocks of executable code that may be used to help initialize a class.

Constructors (8.8) are similar to methods, but cannot be invoked directly by a method call; they are used to initialize new class instances. Like methods, they may be overloaded (8.8.8).

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. 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 may be 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.

Editorial: Note that in the existing JLS, where static context is used to also cover what we now call an early constructor context, this means that, for example, an anonymous class declared as an argument to an explicit constructor invocation is considered to appear in a static context. For example:

class Outer {
    int x;
    class Inner extends Thread {
        Inner() {
            super(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello from " + Outer.this.x); // error??
                }
            });
        }
    }
}

The existing JLS is unclear whether the reference Outer.this.x is valid, as the anonymous class does not have an immediately enclosing instance. (The reference compiler accepts this code.)

Local and anonymous classes that occur in an early construction context are now defined to always have an immediately enclosing instance. Rules specify what can be referenced from the early construction context of a particular class. In the example above, the anonymous class is not in the early construction context of the class Outer and so the field x can be referenced.

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.

When an inner class contains a valid reference 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

A constructor body is a block of code that is executed as part of the process of creating a new instance of a class (12.5). A constructor body may contain an explicit invocation of another constructor of the same class or of the direct superclass (8.8.7.1).

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

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.

Editorial: The paragraph above has been moved to 8.8.7.1

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 prologue of a constructor body may be empty. The BlockStatements in a constructor with no explicit constructor invocation and the BlockStatements following an explicit constructor invocation in a constructor body are called the epilogue. The epilogue of a constructor body may also be empty.

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 the direct superclass that takes no arguments.

Except for the possibility of explicit or implicit 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).

Note that a constructor body contains at most one explicit constructor invocation. The grammar makes it impossible, for example, to place explicit constructor invocations in different branches of an if statement.

A return statement (14.17) may be used in the body of a constructor if it does not include an expression.

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 invocations are divided into two kinds:

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

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

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

The rules for a superclass constructor invocation, where Let C be is the class being instantiated, and let S be is the direct superclass of C, are as follows:

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

An expression occurs in the early construction context of a class C if it is contained in either the prologue of a constructor body of C, or it is nested in the explicit constructor invocation of a constructor body of C.

Expressions that occur in the early construction context of a class C are restricted in the following ways:

  • all unqualified this expressions (15.8.3) and any qualified this expression whose qualifier names the class C are disallowed (15.8.4), except as the qualifier of a permitted instance variable reference in an assignment.

  • Field accesses, method invocations, and method references may not be qualified by super (15.11.2, 15.12.3, 15.13.1).

  • References to instance variables of class C are disallowed, except in an assignment to an instance variable of C whose declaration lacks an initializer (6.5.6.1).

  • Invocations of instance methods of class C are disallowed (15.12.3).

  • Class instance creation expressions that instantiate inner classes may not have immediately enclosing instances of class C (15.9.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 Invocations

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

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.

8.10 Record Classes

8.10.4 Record Constructor Declarations

To ensure proper initialization of its record components, a record class does not implicitly declare a default constructor (8.8.9). Instead, a record class has a canonical constructor, declared explicitly or implicitly, that initializes all the component fields of the record class.

There are two ways to explicitly declare a canonical constructor in a record declaration: by declaring a normal constructor with a suitable signature (8.10.4.1) or by declaring a compact constructor (8.10.4.2).

Given the signature of a normal constructor that qualifies as canonical, and the signature derived for a compact constructor, the rules of constructor signatures (8.8.2) mean it is a compile-time error if a record declaration has both a normal constructor that qualifies as canonical and a compact constructor.

Either way, an explicitly declared canonical constructor must provide at least as much access as the record class, as follows:

An explicitly declared canonical constructor may be a fixed arity constructor or a variable arity constructor (8.8.1).

If a canonical constructor is not explicitly declared in the declaration of a record class R, then a canonical constructor r is implicitly declared in R with the following properties:

The derived formal parameter list of a record class is formed by deriving a formal parameter from each record component in the record header, in order, as follows:

A record declaration may contain declarations of constructors that are not canonical constructors. The body of every non-canonical constructor in a record declaration must start with contain an alternate constructor invocation (8.8.7.1), or a compile-time error occurs.

Chapter 11: Exceptions

11.2 Compile-Time Checking of Exceptions

11.2.2 Exception Analysis of Statements and Explicit Constructor Invocations

A throw statement (14.18) whose thrown expression has static type E and is not a final or effectively final exception parameter can throw E or any exception class that the thrown expression can throw.

For example, the statement throw new java.io.FileNotFoundException(); can throw java.io.FileNotFoundException only. Formally, it is not the case that it "can throw" a subclass or superclass of java.io.FileNotFoundException.

A throw statement whose thrown expression is a final or effectively final exception parameter of a catch clause C can throw an exception class E iff:

A try statement (14.20) can throw an exception class E iff either:

An explicit constructor invocation statement (8.8.7.1) can throw an exception class E iff either:

A switch statement (14.11) can throw an exception class E iff either:

Any other statement S can throw an exception class E iff an expression or statement immediately contained in S can throw E.

An explicit constructor invocation (8.8.7.1) can throw an exception class E iff either:

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.

  3. This constructor does not begin with 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 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 steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, continue with step 4.

  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.

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

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

  2. If this constructor does not contain an explicit constructor invocation (8.8.7.1) then continue from step 5.

  3. Execute the BlockStatements, if any, 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 the next step.

  4. The explicit constructor invocation is either an invocation of another constructor in the same class (using this) or an invocation of a superclass constructor (using super). Evaluate the arguments of the constructor invocation and process the constructor invocation recursively using these same seven steps. If the constructor invocation completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, continue from step 7 if the invocation is of another constructor in the same class, and continue from step 6 if the invocation is of a superclass constructor.

  5. If this constructor is for a class other than Object, then this constructor contains an implicit invocation of a superclass constructor with no arguments. In this case, process the implicit constructor invocation recursively using these same seven steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason, otherwise continue with the next step.

  6. 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 the next step.

  7. Execute the BlockStatements, if any, of the epilogue of this constructor. If execution of any statement 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. Classes can avoid unwanted exposure of uninitialized state by assigning to their fields in the prologue of the constructor body.

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 epilogue 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 epilogue, 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.

Example 12.5-3. Initialization of Fields in the Prologue

class Super {
    Super() { printThree(); }
    void printThree() { System.out.println("three"); }
}
class Test extends Super {
    int three;

    public Test() {
        three = (int)Math.PI;  // That is, 3
        super();
    }

    void printThree() { System.out.println(three); }

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

This alternative to Example 12.5-2 produces the output:

3
3

Because the field three is initialized in the prologue of the Test class's constructor, its assignment occurs in Step 3 of the object initialization procedure, before evaluation of the Super class's constructor body in Step 4. When three is initialized in this way, it is impossible to observe it with the default value 0.

Chapter 14: Blocks, Statements, and Patterns

14.17 The return Statement

A return statement returns control to the invoker of a method (8.4, 15.12) or constructor (8.8, 15.9).

ReturnStatement:
return [Expression] ;

There are two kinds of return statement:

A return statement attempts to transfer control to the invoker of the innermost enclosing constructor, method, or lambda expression; this enclosing declaration or expression is called the return target. In the case of a return statement with value Expression, the value of the Expression becomes the value of the invocation.

It is a compile-time error if a return statement has no return target.

It is a compile-time error if the return target contains either (i) an instance or static initializer that encloses the return statement, or (ii) a switch expression that encloses the return statement.

It is a compile-time error if the return target of a return statement with no value is a method, and that method is not declared void.

It is a compile-time error if the return target of a return statement is a constructor, and the return statement appears in the prologue of this constructor (8.8.7).

It is a compile-time error if the return target of a return statement with value Expression is either a constructor, or a method that is declared void.

It is a compile-time error if the return target of a return statement with value Expression is a method with declared return type T, and the type of Expression is not assignable compatible (5.2) with T.

Execution of a return statement with no value always completes abruptly, the reason being a return with no value.

Execution of a return statement with value Expression first evaluates the Expression. If the evaluation of the Expression completes abruptly for some reason, then the return statement completes abruptly for that reason. If evaluation of the Expression completes normally, producing a value V, then the return statement completes abruptly, the reason being a return with value V.

It can be seen, then, that a return statement always completes abruptly.

The preceding descriptions say "attempts to transfer control" rather than just "transfers control" because if there are any try statements (14.20) within the method or constructor whose try blocks or catch clauses contain the return statement, then any finally clauses of those try statements will be executed, in order, innermost to outermost, before control is transferred to the invoker of the method or constructor. Abrupt completion of a finally clause can disrupt the transfer of control initiated by a return statement.

14.22 Unreachable Statements

It is a compile-time error if a statement cannot be executed because it is unreachable.

This section is devoted to a precise explanation of the word "reachable." The idea is that there must be some possible execution path from the beginning of the constructor, method, instance initializer, or static initializer that contains the statement to the statement itself. The analysis takes into account the structure of statements. Except for the special treatment of while, do, and for statements whose condition expression has the constant value true, the values of expressions are not taken into account in the flow analysis.

For example, a Java compiler will accept the code:

{
    int n = 5;
    while (n > 7) k = 2;
}

even though the value of n is known at compile time and in principle it can be known at compile time that the assignment to k can never be executed.

The rules in this section define two technical terms:

The rules allow a statement to complete normally only if it is reachable.

Two further technical terms are used:

The rules are as follows:

Editorial: The rest of section 14.22 is unchanged.

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 invocations (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).

Let C by the innermost enclosing class or interface declaration of a this expression.

It is a compile-time error if a this expression occurs in an early construction context (8.8.7.1) of C, unless it appears as the qualifier of a field access expression (15.11) appearing as the left-hand operand of a simple assignment expression (15.26).

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.8.4 Qualified this

Any lexically enclosing instance (8.1.3) can be referred to by explicitly qualifying the keyword this.

Let n be an integer such that TypeName denotes the n'th lexically enclosing class or interface declaration of the class or interface whose declaration immediately encloses the qualified this expression.

The value of a qualified this expression TypeName.this is the n'th lexically enclosing instance of this.

If TypeName denotes a generic class, with type parameters F1,...,Fn, the type of the qualified this expression is TypeName<F1,...,Fn>. Otherwise, the type of the qualified this expression is TypeName.

It is a compile-time error if a qualified this expression occurs in a static context (8.1.3).

It is a compile-time error if a qualified this expression occurs in an early construction context (8.8.7.1) of the class named by TypeName.

It is a compile-time error if the class or interface whose declaration immediately encloses a qualified this expression is not an inner class of TypeName or TypeName itself.

15.9 Class Instance Creation Expressions

15.9.2 Determining Enclosing Instances

Let C be the class being instantiated, and let i be the instance being created. If C is an inner class, then i may have an immediately enclosing instance (8.1.3), determined as follows:

If C is an anonymous class, and its direct superclass S is an inner class, then i may have an immediately enclosing instance with respect to S, determined as follows:

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 an early construction context (8.8.7.1) of the current class.

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 an early construction context (8.8.7.1) of the current class.

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 an early construction context (8.8.7.1) of the current class.

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.

Chapter 16: Definite Assignment

Every local variable declared by a statement (14.4.2, 14.14.1, 14.14.2, 14.20.3) and every blank final field (4.12.4, 8.3.1.2) must have a definitely assigned value when any access of its value occurs.

An access to its value consists of the simple name of the variable (or, for a field, the simple name of the field qualified by this) occurring anywhere in an expression except as the left-hand operand of the simple assignment operator = (15.26.1).

For every access of a local variable declared by a statement x, or blank final field x, x must be definitely assigned before the access, or a compile-time error occurs.

Similarly, every blank final variable must be assigned at most once; it must be definitely unassigned when an assignment to it occurs.

Such an assignment is defined to occur if and only if either the simple name of the variable (or, for a field, its simple name qualified by this) occurs on the left hand side of an assignment operator.

For every assignment to a blank final variable, the variable must be definitely unassigned before the assignment, or a compile-time error occurs.

Similarly, for every alternate constructor invocation (8.8.7.1) occurring in a constructor of a class C, every blank final instance variable of C declared in C must be definitely unassigned after the argument list of the alternate constructor invocation, or a compile-time error occurs.

Note that local variables declared by a pattern (14.30) are not subject to the rules of definite assignment. Every local variable declared by a pattern is initialized by the process of pattern matching and so always has a value when accessed.

The remainder of this chapter is devoted to a precise explanation of the words "definitely assigned before" and "definitely unassigned before".

...

16.2 Definite Assignment and Statements

16.2.2 Blocks

Editorial: Note that the body of a constructor cannot be properly considered a block, especially now that it is split into separate prologue and epilogue sections. The treatment of constructor bodies is now covered in 16.9.

We say that V is definitely unassigned everywhere in a block B iff:

These conditions are counterintuitive and require some explanation. Consider a simple assignment V = e. If V is definitely assigned after e, then either:

So, we can conclude that if the conditions are met by a program that causes no compile time error, then any assignments to V in B will not actually take place at run time.

16.9 Definite Assignment, Constructors, and Instance Initializers

Let C be a class declared within the scope of V. Then:

Let C be a class, and let V be a blank final non-static member field of C, declared in C. Then:

Let C be a class, and let V be a blank final non-static member field of C, declared in a superclass of C. Then:

Let C be a class, and let V be a blank final static member field of C. Then:

Let C be a class, and let V be a local variable declared by a statement S contained by a constructor or instance variable initializer of C. Then:

The following rules hold within the constructors (8.8.7) of class C:

Let C be a class, and let V be a blank final member field of C, declared in a superclass of C. Then: