This specification is not final and is subject to change. Use is subject to license terms.

Local and Nested Static Declarations

Changes to the Java® Language Specification • Version 16-internal+0-adhoc.gbierman.20201104

This document describes changes to the Java Language Specification as modified by Consistent Class and Interface Terminology to support static declarations in two new positions:

Nested static declarations have no access to enclosing instances, local variables, or type parameters. This is addressed in the rules for references to variables (6.5.6.1), types (6.5.5.1), methods (15.12.3), and this (15.8.3, 15.9.2, 15.11.2, etc.) to handle references from new contexts.

These enhancements are part of the Records feature. See the JEP Draft for additional details.

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

Chapter 1: Introduction

1.1 Organization of the Specification

...

Chapter 6 describes declarations and names, and how to determine what names mean (that is, which declaration a name denotes). The Java programming language does not require classes and interfaces, or their members, to be declared before they are used. Declaration order is significant only for local variables, local classes and interfaces, and the order of field initializers in a class or interface. Recommended naming conventions that make for more readable programs are described here.

...

Chapter 6: Names

6.1 Declarations

A declaration introduces an entity into a program and includes an identifier (3.8) that can be used in a name to refer to this entity. The identifier is constrained to be a type identifier when the entity being introduced is a class, interface, or type parameter.

A declared entity is one of the following:

Constructors (8.8) are also introduced by declarations, but use the name of the class in which they are declared rather than introducing a new name.

...

6.3 Scope of a Declaration

The scope of a declaration is the region of the program within which the entity declared by the declaration can be referred to using a simple name, provided it is not shadowed (6.4.1).

A declaration is said to be in scope at a particular point in a program if and only if the declaration's scope includes that point.

...

The scope of a local class or interface declaration immediately enclosed by a block (14.2) is the rest of the immediately enclosing block, including its own class declaration the class or interface declaration itself.

The scope of a local class or interface declaration immediately enclosed by a switch block statement group (14.11) is the rest of the immediately enclosing switch block statement group, including its own class declaration the class or interface declaration itself.

The scope of a local variable declaration in a block (14.4) is the rest of the block in which the declaration appears, starting with its own initializer and including any further declarators to the right in the local variable declaration statement.

The scope of a local variable declared in the ForInit part of a basic for statement (14.14.1) includes all of the following:

The scope of a local variable declared in the FormalParameter part of an enhanced for statement (14.14.2) is the contained Statement.

The scope of a parameter of an exception handler that is declared in a catch clause of a try statement (14.20) is the entire block associated with the catch.

The scope of a variable declared in the ResourceSpecification of a try-with-resources statement (14.20.3) is from the declaration rightward over the remainder of the ResourceSpecification and the entire try block associated with the try-with-resources statement.

The translation of a try-with-resources statement implies the rule above.

...

6.4 Shadowing and Obscuring

A local variable (14.4), formal parameter (8.4.1, 15.27.1), exception parameter (14.20), and or local class or interface (14.3) can only be referred to using a simple name, not a qualified name (6.2).

Some declarations are not permitted within the scope of a local variable, formal parameter, exception parameter, or local class or interface declaration because it would be impossible to distinguish between the declared entities using only simple names.

For example, if the name of a formal parameter of a method could be redeclared as the name of a local variable in the method body, then the local variable would shadow the formal parameter and there would be no way to refer to the formal parameter - an undesirable outcome.

It is a compile-time error if the name of a formal parameter is used to declare a new variable within the body of the method, constructor, or lambda expression, unless the new variable is declared within a class or interface declaration contained by the method, constructor, or lambda expression.

It is a compile-time error if the name of a local variable v is used to declare a new variable within the scope of v, unless the new variable is declared within a class whose declaration is or interface declaration appearing within the scope of v.

It is a compile-time error if the name of an exception parameter is used to declare a new variable within the Block of the catch clause, unless the new variable is declared within a class or interface declaration contained by the Block of the catch clause.

It is a compile-time error if the name of a local class or interface C is used to declare a new local class or interface within the scope of C, unless the new local class or interface is declared within another class whose declaration is a class or interface declaration appearing within the scope of C.

These rules allow redeclaration of a variable, or local class, or local interface in nested class or interface declarations that occur in the scope of the variable, or local class, or local interface; such nested class or interface declarations may be local classes class or interface declarations (14.3) or anonymous classes class declarations (15.9 15.9.5). Thus, the declaration of a formal parameter, local variable, or local class, or local interface may be shadowed in a class or interface declaration nested within a method, constructor, or lambda expression; and the declaration of an exception parameter may be shadowed in a class or interface declaration nested within the Block of the catch clause.

There are two design alternatives for handling name clashes created by lambda parameters and other variables declared in lambda expressions. One is to mimic class declarations: like local classes, lambda expressions introduce a new "level" for names, and all variable names outside the expression can be redeclared. Another is a "local" strategy: like catch clauses, for loops, and blocks, lambda expressions operate at the same "level" as the enclosing context, and local variables outside the expression cannot be shadowed. The above rules use the local strategy; there is no special dispensation that allows a variable declared in a lambda expression to shadow a variable declared in an enclosing method.

Note that the rule for local classes does not make an exception for a class of the same name declared within the local class itself. However, this case is prohibited by a separate rule: a class cannot have the same name as a class that encloses it (8.1).

Rephrased the rule so that it doesn't require extensive explanation of the world "another". (The bottom line is that, whatever we say here, 8.1 has already decided that this choice of names is an error.)

Example 6.4-1. Attempted Shadowing Of A Local Variable

Because a declaration of an identifier as a local variable of a method, constructor, or initializer block must not appear within the scope of a parameter or local variable of the same name, a compile-time error occurs for the following program:

class Test1 {
    public static void main(String[] args) {
        int i;
        for (int i = 0; i < 10; i++)
            System.out.println(i);
    }
}

This restriction helps to detect some otherwise very obscure bugs. A similar restriction on shadowing of members by local variables was judged impractical, because the addition of a member in a superclass could cause subclasses to have to rename local variables. Related considerations make restrictions on shadowing of local variables by members of nested classes, or on shadowing of local variables by local variables declared within nested classes unattractive as well.

Hence, the following program compiles without error:

class Test2 {
    public static void main(String[] args) {
        int i;
        class Local {
            {
                for (int i = 0; i < 10; i++)
                    System.out.println(i);
            }
        }
        new Local();
    }
}

On the other hand, local variables with the same name may be declared in two separate blocks or for statements, neither of which contains the other:

class Test3 {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++)
            System.out.print(i + " ");
        for (int i = 10; i > 0; i--)
            System.out.print(i + " ");
        System.out.println();
    }
}

This program compiles without error and, when executed, produces the output:

0 1 2 3 4 5 6 7 8 9 10 9 8 7 6 5 4 3 2 1

6.4.2 Obscuring

...

Obscuring involving field names is rare; however:

...

6.5 Determining the Meaning of a Name

6.5.2 Reclassification of Contextually Ambiguous Names

An AmbiguousName is then reclassified as follows.

If the AmbiguousName is a simple name, consisting of a single Identifier:

If the AmbiguousName is a qualified name, consisting of a name, a ".", and an Identifier, then the name to the left of the "." is first reclassified, for it is itself an AmbiguousName. There is then a choice:

The requirement that a potential type name be "a valid TypeIdentifier" prevents treating var and yield as a type name. It is usually redundant, because the rules for declarations already prevent the introduction of types named var and yield. However, in some cases, a compiler may find a binary class named var or yield, and we want to be clear that such classes can never be named. The simplest solution is to consistently check for a valid TypeIdentifier.

Example 6.5.2-1. Reclassification of Contextually Ambiguous Names

Consider the following contrived "library code":

package org.rpgpoet;
import java.util.Random;
public interface Music { Random[] wizards = new Random[4]; }

and then consider this example code in another package:

package bazola;
class Gabriel {
    static int n = org.rpgpoet.Music.wizards.length;
}

First of all, the name org.rpgpoet.Music.wizards.length is classified as an ExpressionName because it functions as a PostfixExpression. Therefore, each of the names:

org.rpgpoet.Music.wizards
org.rpgpoet.Music
org.rpgpoet
org

is initially classified as an AmbiguousName. These are then reclassified:

6.5.5 Meaning of Type Names

6.5.5.1 Simple Type Names

If a type name consists of a single Identifier, then the identifier must occur in the scope of exactly one declaration of a class, interface, or type parameter with this name (6.3), or a compile-time error occurs.

If the declaration denotes a type parameter of a class or interface C (8.1.2, 9.1.2), then both of the following must be true:

Otherwise, a compile-time error occurs.

If the declaration denotes a type parameter of a method or constructor m (8.4.4, 8.8.4) declared by a class or interface C, then both of the following must be true:

Otherwise, a compile-time error occurs.

The meaning of the type name is that the in-scope class, interface, or type parameter.

Example 6.5.5.1-1. References to Type Parameters

class Box<T> {

    T val;

    T get() {
        return val;
    }

    static Box<T> empty() { // compile-time error
        return new Box<>(null);
    }

    static <U> Box<U> make(U val) {
        interface Checker {
            void check(U val); // compile-time error
        }
        class NullChecker implements Checker {
            void check(U val) {
                if (val == null) {
                    throw new IllegalArgumentException();
                }
            }
        }
        new NullChecker().check(val);
        return new Box<U>(val);
    }
}

The class type parameter T is in scope throughout the declaration of class Box; however, using the name T in the static method declaration empty is illegal.

Similarly, the method type parameter U is in scope throughout the declaration of method make; however, using the name U in the (implicitly static) local interface declaration Checker is illegal.

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), the expression name must appear within an instance method (8.4.3.2), instance variable initializer (8.3.2), instance initializer (8.6), or constructor (8.8). If the expression name appears within a static type declaration, class method, class variable initializer, or static initializer (8.7), then both of the following must be true:

Otherwise, a compile-time error occurs.

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 enclosing the variable declaration, and let C be the immediately enclosing type declaration of X. Then both of the following must be true:

Otherwise, a compile-time error occurs.

In addition, 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 within an inner class or lambda expression (15.27) contained by X.

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 InstanceVariableTest {
    static String a;
    String b;

    String concat() {
        return a + b;
    }

    static String staticConcat() {
        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();
        matcher.match(a, b);
    }

}

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

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

class LocalVariableTest {

    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 in checkFirst is not an inner class of Checker, so using the name first in the anonymous class body is illegal.

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 Identifier which specifies the name of the method to be invoked. The rules of method invocation (15.12) require that the Identifier either denotes a method that is in scope at the point of the method invocation, or denotes a method imported by a single-static-import declaration or static-import-on-demand declaration (7.5.3, 7.5.4). The rules also prohibit (15.12.3) a reference to an instance method occurring in a static context (8.1.3) or from a nested class or interface other than an inner class of the class or interface declaring m.

The details of the restriction on references to instance methods appear in 15.12.3, but it's useful to mention them here, for consistency with 6.5.5.1 and 6.5.6.1.

The clause about imports is unnecessary: the scoping rules (6.3) already establish that such methods are in scope.

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

6.7 Fully Qualified Names and Canonical Names

Every primitive type, named package, top level class, and top level interface has a fully qualified name:

Each member class, member interface, and array type may have a fully qualified name:

A local class, local interface, or anonymous class does not have a fully qualified name.

Every primitive type, named package, top level class, and top level interface has a canonical name:

Each member class, member interface, and array type may have a canonical name:

A local class, local interface, or anonymous class does not have a canonical name.

Example 6.7-1. Fully Qualified Names

In the code:

package points;
class Point    { int x, y; }
class PointVec { Point[] vec; }

the fully qualified name of the type Point is "points.Point"; the fully qualified name of the type PointVec is "points.PointVec"; and the fully qualified name of the type of the field vec of class PointVec is "points.Point[]".

Example 6.7-2. Fully Qualified Names v. Canonical Name

The difference between a fully qualified name and a canonical name can be seen in code such as:

package p;
class O1 { class I {} }
class O2 extends O1 {}

Both p.O1.I and p.O2.I are fully qualified names that denote the member class I, but only p.O1.I is its canonical name.

Chapter 8: Classes

8.1 Class Declarations

8.1.1 Class Modifiers

A class declaration may include class modifiers.

ClassModifier:
(one of)
Annotation public protected private
abstract static final strictfp

The rules for annotation modifiers on a class declaration are specified in 9.7.4 and 9.7.5.

The access modifier public (6.6) pertains only to top level classes (7.6) and member classes (8.5, 9.5).

The access modifiers protected, and private, and static pertain only to member classes.

The modifier static pertains only to member classes and local classes (14.3).

It is a compile-time error if the same keyword appears more than once as a modifier for a class declaration, or if a class declaration has more than one of the access modifiers public, protected, and private (6.6).

If two or more (distinct) class modifiers appear in a class declaration, then it is customary, though not required, that they appear in the order consistent with that shown above in the production for ClassModifier.

8.1.1.1 abstract Classes

An abstract class is a class that is incomplete, or to be considered incomplete.

It is a compile-time error if an attempt is made to create an instance of an abstract class using a class instance creation expression (15.9.1).

...

8.1.1.2 final Classes

A class can be declared final if its definition is complete and no subclasses are desired or required.

It is a compile-time error if the name of a final class appears in the extends clause (8.1.4) of another class declaration; this implies that a final class cannot have any subclasses.

It is a compile-time error if a class is declared both final and abstract, because the implementation of such a class could never be completed (8.1.1.1).

Because a final class never has any subclasses, the methods of a final class are never overridden (8.4.8.1).

8.1.1.3 strictfp Classes

The effect of the strictfp modifier is to make all float or double expressions within the class declaration (including within variable initializers, instance initializers, static initializers, and constructors) be explicitly FP-strict (15.4).

This implies that all methods declared in the class, and all nested classes and interfaces declared in the class, are implicitly strictfp.

8.1.1.4 static Classes

The static keyword indicates that a nested class is not an inner class (8.1.3). The class has no immediately enclosing instance and cannot directly reference enclosing type variables (6.5.5.1); enclosing instance variables, local variables, formal parameters, or exception parameters (6.5.6.1); or enclosing instance methods (15.12.3).

A local class declaration may not use the static keyword (14.3).

Nested enum classes are implicitly declared static. A member enum class may redundantly specify the static modifier; a local enum class may not (8.9).

8.1.2 Generic Classes and Type Parameters

A class is generic if it declares one or more type variables (4.4).

These type variables are known as the type parameters of the class. The type parameter section follows the class name and is delimited by angle brackets.

TypeParameters:
< TypeParameterList >
TypeParameterList:
TypeParameter {, TypeParameter}

The following productions from 4.4 are shown here for convenience:

TypeParameter:
{TypeParameterModifier} TypeIdentifier [TypeBound]
TypeParameterModifier:
Annotation
TypeBound:
extends TypeVariable
extends ClassOrInterfaceType {AdditionalBound}
AdditionalBound:
& InterfaceType

The rules for annotation modifiers on a type parameter declaration are specified in 9.7.4 and 9.7.5.

In a class's type parameter section, a type variable T directly depends on a type variable S if S is the bound of T, while T depends on S if either T directly depends on S or T directly depends on a type variable U that depends on S (using this definition recursively). It is a compile-time error if a type variable in a class's type parameter section depends on itself.

The scope and shadowing of a class's type parameter is specified in 6.3 and 6.4.

References to a class's type parameter from a static context or a nested class are restricted, as specified in 6.5.5.1.

A generic class declaration defines a set of parameterized types (4.5), one for each possible parameterization of the type parameter section by type arguments. All of these parameterized types share the same class at run time.

For instance, executing the code:

Vector<String>  x = new Vector<String>();
Vector<Integer> y = new Vector<Integer>();
boolean b = x.getClass() == y.getClass();

will result in the variable b holding the value true.

It is a compile-time error if a generic class is a direct or indirect subclass of Throwable (11.1.1).

This restriction is needed since the catch mechanism of the Java Virtual Machine works only with non-generic classes.

It is a compile-time error to refer to a type parameter of a generic class C in any of the following:

These restrictions are now specified in 6.5.5.1, directly at the point where the reference occurs.

Example 8.1.2-1. Mutually Recursive Type Variable Bounds

interface ConvertibleTo<T> {
    T convert();
}
class ReprChange<T extends ConvertibleTo<S>,
                 S extends ConvertibleTo<T>> {
    T t;
    void set(S s) { t = s.convert();    }
    S get()       { return t.convert(); }
}

Example 8.1.2-2. Nested Generic Classes

class Seq<T> {
    T      head;
    Seq<T> tail;

    Seq() { this(null, null); }
    Seq(T head, Seq<T> tail) {
        this.head = head;
        this.tail = tail;
    }
    boolean isEmpty() { return tail == null; }

    class Zipper<S> {
        Seq<Pair<T,S>> zip(Seq<S> that) {
            if (isEmpty() || that.isEmpty()) {
                return new Seq<Pair<T,S>>();
            } else {
                Seq<T>.Zipper<S> tailZipper =
                    tail.new Zipper<S>();
                return new Seq<Pair<T,S>>(
                    new Pair<T,S>(head, that.head),
                    tailZipper.zip(that.tail));
            }
        }
    }
}
class Pair<T, S> {
    T fst; S snd;
    Pair(T f, S s) { fst = f; snd = s; }
}
class Test {
    public static void main(String[] args) {
        Seq<String> strs =
            new Seq<String>(
                "a",
                new Seq<String>("b",
                                new Seq<String>()));
        Seq<Number> nums =
            new Seq<Number>(
                new Integer(1),
                new Seq<Number>(new Double(1.5),
                                new Seq<Number>()));

        Seq<String>.Zipper<Number> zipper =
            strs.new Zipper<Number>();

        Seq<Pair<String,Number>> combined =
            zipper.zip(nums);
    }
}

8.1.3 Inner Classes and Enclosing Instances

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

An inner class may be a non-static member class (8.5), a non-static local class (14.3), or an anonymous class (15.9.5). A member class of an interface is implicitly static (9.5) so is never considered to be an inner class. Nested interfaces (9.1), nested enum classes (8.9), member annotation interfaces (9.6), and member classes of interfaces (9.5) are implicitly static, so are never considered to be inner classes.

It is a compile-time error if an inner class declares a static initializer (8.7).

It is a compile-time error if an inner class declares a member that is explicitly or implicitly static, unless the member is a constant variable (4.12.4).

An inner class may inherit static members that are not constant variables even though it cannot declare them.

A nested class that is not an inner class may declare static members freely, in accordance with the usual rules of the Java programming language.

Example 8.1.3-1. Inner Class Declarations and Static Members

class HasStatic {
    static int j = 100;
}
class Outer {
    class Inner extends HasStatic {
        static final int x = 3;  // OK: constant variable
        static int y = 4;  // Compile-time error: an inner class
    }
    static class NestedButNotInner{
        static int z = 5;    // OK: not an inner class
    }
    interface NeverInner {}  // Interfaces are never inner
}

By eliminating these rules, we permit arbitrary static member declarations within inner classes.

A statement or expression occurs in a static context if and only if the innermost method declaration, constructor declaration, instance initializer, static initializer, field initializer declaration, or explicit constructor invocation statement enclosing the statement or expression is a static method declaration, a static initializer, the variable initializer of a static variable a static field declaration, or an explicit constructor invocation statement (8.8.7.1).

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 type declaration of itself.

A class O is the n'th lexically enclosing type declaration of a class C if it is the immediately enclosing type declaration of the n-1'th lexically enclosing type 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 a an inner local class or an anonymous class whose declaration occurs in a static context has no lexically enclosing instances.

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's 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 declared final or be effectively final (4.12.4), or a compile-time error occurs where the use is attempted.

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.

The restrictions on references to non-final variables are now specified in 6.5.6.1, directly at the point where the reference occurs.

The rules about definite assignment are probably redundant, but that's beyond the scope of this change.

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.3 Field Declarations

8.3.1 Field Modifiers

8.3.1.1 static Fields

If a field is declared static, there exists exactly one incarnation of the field, no matter how many instances (possibly zero) of the class may eventually be created. A static field, sometimes called a class variable, is incarnated when the class is initialized (12.4).

A static field declaration introduces a static context (8.1.3).

A field that is not declared static (sometimes called a non-static field) is called an instance variable. Whenever a new instance of a class is created (12.5), a new variable associated with that instance is created for every instance variable declared in that class or any of its superclasses.

References to an instance variable from a static context or a nested class are restricted, as specified in 6.5.6.1.

Example 8.3.1.1-1. static Fields

class Point {
    int x, y, useCount;
    Point(int x, int y) { this.x = x; this.y = y; }
    static final Point origin = new Point(0, 0);
}
class Test {
    public static void main(String[] args) {
        Point p = new Point(1,1);
        Point q = new Point(2,2);
        p.x = 3;
        p.y = 3;
        p.useCount++;
        p.origin.useCount++;
        System.out.println("(" + q.x + "," + q.y + ")");
        System.out.println(q.useCount);
        System.out.println(q.origin == Point.origin);
        System.out.println(q.origin.useCount);
    }
}

This program prints:

(2,2)
0
true
1

showing that changing the fields x, y, and useCount of p does not affect the fields of q, because these fields are instance variables in distinct objects. In this example, the class variable origin of the class Point is referenced both using the class name as a qualifier, in Point.origin, and using variables of the class type in field access expressions (15.11), as in p.origin and q.origin. These two ways of accessing the origin class variable access the same object, evidenced by the fact that the value of the reference equality expression (15.21.3):

q.origin==Point.origin

is true. Further evidence is that the incrementation:

p.origin.useCount++;

causes the value of q.origin.useCount to be 1; this is so because p.origin and q.origin refer to the same variable.

Example 8.3.1.1-2. Hiding of Class Variables

class Point {
    static int x = 2;
}
class Test extends Point {
    static double x = 4.7;
    public static void main(String[] args) {
        new Test().printX();
    }
    void printX() {
        System.out.println(x + " " + super.x);
    }
}

This program produces the output:

4.7 2

because the declaration of x in class Test hides the definition of x in class Point, so class Test does not inherit the field x from its superclass Point. Within the declaration of class Test, the simple name x refers to the field declared within class Test. Code in class Test may refer to the field x of class Point as super.x (or, because x is static, as Point.x). If the declaration of Test.x is deleted:

class Point {
    static int x = 2;
}
class Test extends Point {
    public static void main(String[] args) {
        new Test().printX();
    }
    void printX() {
        System.out.println(x + " " + super.x);
    }
}

then the field x of class Point is no longer hidden within class Test; instead, the simple name x now refers to the field Point.x. Code in class Test may still refer to that same field as super.x. Therefore, the output from this variant program is:

2 2

Example 8.3.1.1-3. Hiding of Instance Variables

class Point {
    int x = 2;
}
class Test extends Point {
    double x = 4.7;
    void printBoth() {
        System.out.println(x + " " + super.x);
    }
    public static void main(String[] args) {
        Test sample = new Test();
        sample.printBoth();
        System.out.println(sample.x + " " + ((Point)sample).x);
    }
}

This program produces the output:

4.7 2
4.7 2

because the declaration of x in class Test hides the definition of x in class Point, so class Test does not inherit the field x from its superclass Point. It must be noted, however, that while the field x of class Point is not inherited by class Test, it is nevertheless implemented by instances of class Test. In other words, every instance of class Test contains two fields, one of type int and one of type double. Both fields bear the name x, but within the declaration of class Test, the simple name x always refers to the field declared within class Test. Code in instance methods of class Test may refer to the instance variable x of class Point as super.x.

Code that uses a field access expression to access field x will access the field named x in the class indicated by the type of reference expression. Thus, the expression sample.x accesses a double value, the instance variable declared in class Test, because the type of the variable sample is Test, but the expression ((Point)sample).x accesses an int value, the instance variable declared in class Point, because of the cast to type Point.

If the declaration of x is deleted from class Test, as in the program:

class Point {
    static int x = 2;
}
class Test extends Point {
    void printBoth() {
        System.out.println(x + " " + super.x);
    }
    public static void main(String[] args) {
        Test sample = new Test();
        sample.printBoth();
        System.out.println(sample.x + " " + ((Point)sample).x);
    }
}

then the field x of class Point is no longer hidden within class Test. Within instance methods in the declaration of class Test, the simple name x now refers to the field declared within class Point. Code in class Test may still refer to that same field as super.x. The expression sample.x still refers to the field x within type Test, but that field is now an inherited field, and so refers to the field x declared in class Point. The output from this variant program is:

2 2
2 2

8.3.2 Field Initialization

If a declarator in a field declaration has a variable initializer, then the declarator has the semantics of an assignment (15.26) to the declared variable.

If the declarator is for a class variable (that is, a static field), then the following rules apply to its initializer: at run time, the initializer is evaluated and the assignment performed exactly once, when the class is initialized (12.4.2).

Note that static fields that are constant variables (4.12.4) are initialized before other static fields (12.4.2). This also applies in interfaces (9.3.1). When such fields are referenced by simple name, they will never be observed to have their default initial values (4.12.5).

The restrictions on references appear in 6.5.6.1, 15.8.3, 15.11.2, and 15.12.3, directly at the point where the reference occurs.

If the declarator is for an instance variable (that is, a field that is not static), then the following rules apply to its initializer: at run time, the initializer is evaluated and the assignment performed each time an instance of the class is created (12.5).

References from variable initializers to fields that may not yet be initialized are subject to additional restrictions restricted, as specified in 8.3.3 and 16.

Exception checking for a variable initializer in a field declaration is specified in 11.2.3.

Variable initializers are also used in local variable declaration statements (14.4), where the initializer is evaluated and the assignment performed each time the local variable declaration statement is executed.

Example 8.3.2-1. Field Initialization

class Point {
    int x = 1, y = 5;
}
class Test {
    public static void main(String[] args) {
        Point p = new Point();
        System.out.println(p.x + ", " + p.y);
    }
}

This program produces the output:

1, 5

because the assignments to x and y occur whenever a new Point is created.

Example 8.3.2-2. Forward Reference to a Class Variable

class Test {
    float f = j;
    static int j = 1;
}

This program compiles without error; it initializes j to 1 when class Test is initialized, and initializes f to the current value of j every time an instance of class Test is created.

8.4 Method Declarations

8.4.1 Formal Parameters

The formal parameters of a method or constructor, if any, are specified by a list of comma-separated parameter specifiers. Each parameter specifier consists of a type (optionally preceded by the final modifier and/or one or more annotations) and an identifier (optionally followed by brackets) that specifies the name of the parameter.

If a method or constructor has no formal parameters, and no receiver parameter, then an empty pair of parentheses appears in the declaration of the method or constructor.

FormalParameterList:
FormalParameter {, FormalParameter}
FormalParameter:
{VariableModifier} UnannType VariableDeclaratorId
VariableArityParameter
VariableArityParameter:
{VariableModifier} UnannType {Annotation} ... Identifier
VariableModifier:
Annotation
final

The following productions from 8.3 and 4.3 are shown here for convenience:

VariableDeclaratorId:
Identifier [Dims]
Dims:
{Annotation} [ ] {{Annotation} [ ]}

A formal parameter of a method or constructor may be a variable arity parameter, indicated by an ellipsis following the type. At most one variable arity parameter is permitted for a method or constructor. It is a compile-time error if a variable arity parameter appears anywhere in the list of parameter specifiers except the last position.

In the grammar for VariableArityParameter, note that the ellipsis (...) is a token unto itself (3.11). It is possible to put whitespace between it and the type, but this is discouraged as a matter of style.

If the last formal parameter is a variable arity parameter, the method is a variable arity method. Otherwise, it is a fixed arity method.

The rules for annotation modifiers on a formal parameter declaration and on a receiver parameter are specified in 9.7.4 and 9.7.5.

It is a compile-time error if final appears more than once as a modifier for a formal parameter declaration.

The scope and shadowing of a formal parameter is specified in 6.3 and 6.4.

References to a formal parameter from a nested class or a lambda expression are restricted, as specified in 6.5.6.1.

It is a compile-time error for a method or constructor to declare two formal parameters with the same name. (That is, their declarations mention the same Identifier.)

It is a compile-time error if a formal parameter that is declared final is assigned to within the body of the method or constructor.

The declared type of a formal parameter depends on whether it is a variable arity parameter:

If the declared type of a variable arity parameter has a non-reifiable element type (4.7), then a compile-time unchecked warning occurs for the declaration of the variable arity method, unless the method is annotated with @SafeVarargs (9.6.4.7) or the warning is suppressed by @SuppressWarnings (9.6.4.5).

When the method or constructor is invoked (15.12), the values of the actual argument expressions initialize newly created parameter variables, each of the declared type, before execution of the body of the method or constructor. The Identifier that appears in the FormalParameter may be used as a simple name in the body of the method or constructor to refer to the formal parameter.

Invocations of a variable arity method may contain more actual argument expressions than formal parameters. All the actual argument expressions that do not correspond to the formal parameters preceding the variable arity parameter will be evaluated and the results stored into an array that will be passed to the method invocation (15.12.4.2).

A method's or constructor's formal parameter of type float always contains an element of the float value set (4.2.3); similarly, a method's or constructor's formal parameter of type double always contains an element of the double value set. It is not permitted for a method's or constructor's formal parameter of type float to contain an element of the float-extended-exponent value set that is not also an element of the float value set, nor for a method's or constructor's formal parameter of type double to contain an element of the double-extended-exponent value set that is not also an element of the double value set.

Where an actual argument expression corresponding to a parameter variable is not FP-strict (15.4), evaluation of that actual argument expression is permitted to use intermediate values drawn from the appropriate extended-exponent value sets. Prior to being stored in the parameter variable, the result of such an expression is mapped to the nearest value in the corresponding standard value set by being subjected to invocation conversion (5.3).

Here are some examples of receiver parameters in instance methods and inner classes' constructors:

class Test {
    Test(/* ?? ?? */) {}
      // No receiver parameter is permitted in the constructor of
      // a top level class, as there is no conceivable type or name.

    void m(Test this) {}
      // OK: receiver parameter in an instance method

    static void n(Test this) {}
      // Illegal: receiver parameter in a static method                         

    class A {
        A(Test Test.this) {}
          // OK: the receiver parameter represents the instance
          // of Test which immediately encloses the instance
          // of A being constructed.

        void m(A this) {}
          // OK: the receiver parameter represents the instance
          // of A for which A.m() is invoked.

        class B {
            B(Test.A A.this) {}
              // OK: the receiver parameter represents the instance
              // of A which immediately encloses the instance of B
              // being constructed.

            void m(Test.A.B this) {}
              // OK: the receiver parameter represents the instance
              // of B for which B.m() is invoked.
        }
    }
}

B's constructor and instance method show that the type of the receiver parameter may be denoted with a qualified TypeName like any other type; but that the name of the receiver parameter in an inner class's constructor must use the simple name of the enclosing class.

8.4.3 Method Modifiers

8.4.3.2 static Methods

A method that is declared static is called a class method.

It is a compile-time error to use the name of a type parameter of any surrounding declaration in the header or body of a class method.

A class method is always invoked without reference to a particular object. It is a compile-time error to attempt to refer to the current object using the keyword this (15.8.3) or the keyword super (15.11.2).

A static method declaration introduces a static context (8.1.3).

The restrictions on references appear in 6.5.5.1, 15.8.3, 15.11.2, and 15.12.3, directly at the point where the reference occurs.

A method that is not declared static is called an instance method, and sometimes called a non-static method.

An instance method is always invoked with respect to an object, which becomes the current object to which the keywords this and super refer during execution of the method body (15.8.3, 15.11.2).

References to an instance method from a static context or a nested class are restricted, as specified in 15.12.3.

8.4.4 Generic Methods

A method is generic if it declares one or more type variables (4.4).

These type variables are known as the type parameters of the method. The form of the type parameter section of a generic method is identical to the type parameter section of a generic class (8.1.2).

A generic method declaration defines a set of methods, one for each possible invocation of the type parameter section by type arguments. Type arguments may not need to be provided explicitly when a generic method is invoked, as they can often be inferred (18).

The scope and shadowing of a method's type parameter is specified in 6.3 and 6.4.

References to a method's type parameter from a nested class are restricted, as specified in 6.5.5.1.

Two methods or constructors M and N have the same type parameters if both of the following are true:

Where two methods or constructors M and N have the same type parameters, a type mentioned in N can be adapted to the type parameters of M by applying θ, as defined above, to the type.

8.5 Member Class and Interface Declarations

8.5.1 Static Member Class and Interface Declarations

The static keyword may modify the declaration of a member class C within the body of a non-inner class or interface T. Its effect is to declare that C is not an inner class. Just as a static method of T has no current instance of T in its body, C also has no current instance of T, nor does it have any lexically enclosing instances.

It is a compile-time error if a static class contains a usage of a non-static member of an enclosing class.

A member interface is implicitly static (9.1.1). It is permitted for the declaration of a member interface to redundantly specify the static modifier.

This section has been replaced by 8.1.1.4, which is applicable to both member classes and local enum classes.

Discussions about implicit modifiers occur where the relevant syntax is specified (8.9, 9.1.1, 9.6).

8.7 Static Initializers

A static initializer declared in a class is executed when the class is initialized (12.4.2). Together with any field initializers for class variables (8.3.2), static initializers may be used to initialize the class variables of the class.

StaticInitializer:
static Block

It is a compile-time error if a static initializer cannot complete normally (14.21).

It is a compile-time error if a return statement (14.17) appears anywhere within a static initializer.

It is a compile-time error if the keyword this (15.8.3) or the keyword super (15.11, 15.12) or any type variable declared outside the static initializer, appears anywhere within a static initializer.

A static initializer introduces a static context (8.1.3).

The restrictions on references appear in 6.5.5.1, 15.8.3, 15.11.2, and 15.12.3, directly at the point where the reference occurs.

Restrictions on how a static initializer may refer to class variables, even when the class variables are in scope, are specified in 8.3.3.

Exception checking for a static initializer is specified in 11.2.3.

8.8 Constructor Declarations

8.8.1 Formal Parameters

The formal parameters of a constructor are identical in syntax and semantics to those of a method (8.4.1).

The constructor of a non-private inner member class implicitly declares, as the first formal parameter, a variable representing the immediately enclosing instance of the class (15.9.2, 15.9.3).

The rationale for why only this kind of class has an implicitly declared constructor parameter is subtle. The following explanation may be helpful:

  1. In a class instance creation expression for a non-private inner member class, 15.9.2 specifies the immediately enclosing instance of the member class. The member class may have been emitted by a compiler which is different than the compiler of the class instance creation expression. Therefore, there must be a standard way for the compiler of the creation expression to pass a reference (representing the immediately enclosing instance) to the member class's constructor. Consequently, the Java programming language deems in this section that a non-private inner member class's constructor implicitly declares an initial parameter for the immediately enclosing instance. 15.9.3 specifies that the instance is passed to the constructor.

  2. In a class instance creation expression for a an inner local class (not in a static context) or an anonymous class (not in a static context), 15.9.2 specifies the immediately enclosing instance of the local/anonymous class. The local/anonymous class is necessarily emitted by the same compiler as the class instance creation expression. That compiler can represent the immediately enclosing instance how ever it wishes. There is no need for the Java programming language to implicitly declare a parameter in the local/anonymous class's constructor.

  3. In a class instance creation expression for an anonymous class, and where the anonymous class's superclass is either inner or local an inner class (not in a static context), 15.9.2 specifies the anonymous class's immediately enclosing instance with respect to the superclass. This instance must be transmitted from the anonymous class to its superclass, where it will serve as the immediately enclosing instance. Since the superclass may have been emitted by a compiler which is different than the compiler of the class instance creation expression, it is necessary to transmit the instance in a standard way, by passing it as the first argument to the superclass's constructor. Note that the anonymous class itself is necessarily emitted by the same compiler as the class instance creation expression, so it would be possible for the compiler to transmit the immediately enclosing instance with respect to the superclass to the anonymous class how ever it wishes, before the anonymous class passes the instance to the superclass's constructor. However, for consistency, the Java programming language deems in 15.9.5.1 that, in some circumstances, an anonymous class's constructor implicitly declares an initial parameter for the immediately enclosing instance with respect to the superclass.

The fact that a non-private inner member class may be accessed by a different compiler than compiled it, whereas a an inner local or anonymous class is always accessed by the same compiler that compiled it, explains why the binary name of a non-private inner member class is defined to be predictable but the binary name of a an inner local or anonymous class is not (13.1).

8.8.4 Generic Constructors

A constructor is generic if it declares one or more type variables (4.4).

These type variables are known as the type parameters of the constructor. The form of the type parameter section of a generic constructor is identical to the type parameter section of a generic class (8.1.2).

It is possible for a constructor to be generic independently of whether the class the constructor is declared in is itself generic.

A generic constructor declaration defines a set of constructors, one for each possible invocation of the type parameter section by type arguments. Type arguments may not need to be provided explicitly when a generic constructor is invoked, as they can often by inferred (18).

The scope and shadowing of a constructor's type parameter is specified in 6.3 and 6.4.

References to a constructor's type parameter from an explicit constructor invocation statement or a nested class are restricted, as specified in 6.5.5.1.

8.8.7 Constructor Body

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 in a constructor body may not refer to any instance variables or instance methods or inner classes declared in this class or any superclass, or use this or super in any expression; otherwise, a compile-time error occurs.

This prohibition on using the current instance explains why an explicit constructor invocation statement is deemed to occur in a static context (8.1.3).

An explicit constructor invocation statement introduces a static context (8.1.3).

The restrictions on references appear in 6.5.6.1, 15.8.3, 15.9.2, 15.11.2, and 15.12.3, directly at the point where the reference occurs.

The actual restriction is on implicit or explicit use of this—so, for example, it's fine to refer to an instance variable of another class instance passed in as a parameter, or to cast to an inner class type.

We should explore what javac does here, because the rule is not clearly stated.

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.

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

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

      If S is an inner member class, then 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.

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

8.9 Enum Classes

An enum declaration specifies a new enum class, a special kind of class that defines a small set of named class instances.

EnumDeclaration:
{ClassModifier} enum TypeIdentifier [ClassImplements] EnumBody

An enum declaration may specify a top level enum class (7.6), or a member enum class (8.5, 9.5), or a local enum class (14.3).

It is a compile-time error if an enum declaration has the modifier abstract or final.

An enum declaration is implicitly final unless it contains at least one enum constant that has a class body (8.9.1).

A member nested enum class is implicitly static. It is permitted for the declaration of a member enum class to redundantly specify the static modifier. A local enum declaration may not redundantly specify the static modifier (14.3).

This implies that it is impossible to declare an enum class as a member of an inner class (8.1.3), because an inner class cannot have static members except for constant variables.

It is a compile-time error if the same keyword appears more than once as a modifier for an enum declaration, or if an enum declaration has more than one of the access modifiers public, protected, and private (6.6).

An enum declaration does not have an extends clause. The direct superclass type of an enum class E is Enum<E> (8.1.4).

An enum class has no instances other than those defined by its enum constants. It is a compile-time error to attempt to explicitly instantiate an enum class (15.9.1).

In addition to the compile-time error, three further mechanisms ensure that no instances of an enum class exist beyond those defined by its enum constants:

Chapter 9: Interfaces

An interface declaration introduces a new interface that can be implemented by one or more classes. Programs can use interfaces to provide a common supertype for otherwise-unrelated classes.

Interfaces have no instance variables, and typically declare one or more abstract methods; otherwise unrelated classes can implement an interface by providing implementations for its abstract methods. Interfaces may not be directly instantiated.

A top level interface (7.6) is an interface that is declared at the top level of a compilation unit.

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

An annotation interface (9.6) is an interface declared with special syntax, intended to be implemented by reflective representations of annotations (9.7).

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

An interface may be declared to be a direct extension of one or more other interfaces, meaning that it inherits all the member classes and interfaces, instance methods, and static fields of the interfaces it extends, except for any members that it may override or hide.

A class may be declared to directly implement one or more interfaces (8.1.5), meaning that any instance of the class implements all the abstract methods specified by the interface or interfaces. A class necessarily implements all the interfaces that its direct superclasses and direct superinterfaces do. This (multiple) interface inheritance allows objects to support (multiple) common behaviors without sharing a superclass.

A variable whose declared type is an interface type may have as its value a reference to any instance of a class which implements the specified interface. It is not sufficient that the class happen to implement all the abstract methods of the interface; the class or one of its superclasses must actually be declared to implement the interface, or else the class is not considered to implement the interface.

9.1 Interface Declarations

9.1.1 Interface Modifiers

An interface declaration may include interface modifiers.

InterfaceModifier:
(one of)
Annotation public protected private
abstract static strictfp

The rules for annotation modifiers on an interface declaration are specified in 9.7.4 and 9.7.5.

The access modifier public (6.6) pertains only to top level interfaces (7.6) and member interfaces (8.5, 9.5).

The access modifiers protected, and private, and static pertain only to member interfaces (8.5, 9.5).

The modifier static pertains only to member interfaces and local interfaces (14.3).

It is a compile-time error if the same keyword appears more than once as a modifier for an interface declaration, or if a interface declaration has more than one of the access modifiers public, protected, and private (6.6).

If two or more (distinct) interface modifiers appear in an interface declaration, then it is customary, though not required, that they appear in the order consistent with that shown above in the production for InterfaceModifier.

9.1.1.1 abstract Interfaces

Every interface is implicitly abstract.

This modifier is obsolete and should not be used in new programs.

9.1.1.2 strictfp Interfaces

The effect of the strictfp modifier is to make all float or double expressions within the interface declaration be explicitly FP-strict (15.4).

This implies that all methods declared in the interface, and all nested types declared in the interface, are implicitly strictfp.

9.1.1.3 static Interfaces

A nested interface declaration is implicitly static. It is permitted for the declaration of a member interface to redundantly specify the static modifier. A local interface declaration may not redundantly specify the static modifier (14.3).

Because a nested interface is static, it has no immediately enclosing instance and cannot directly reference enclosing type variables (6.5.5.1); enclosing instance variables, local variables, formal parameters, or exception parameters (6.5.6.1); or enclosing instance methods (15.12.3).

9.1.2 Generic Interfaces and Type Parameters

An interface is generic if it declares one or more type variables (4.4).

These type variables are known as the type parameters of the interface. The type parameter section follows the interface name and is delimited by angle brackets.

The following productions from 8.1.2 and 4.4 are shown here for convenience:

TypeParameters:
< TypeParameterList >
TypeParameterList:
TypeParameter {, TypeParameter}
TypeParameter:
{TypeParameterModifier} TypeIdentifier [TypeBound]
TypeParameterModifier:
Annotation
TypeBound:
extends TypeVariable
extends ClassOrInterfaceType {AdditionalBound}
AdditionalBound:
& InterfaceType

The rules for annotation modifiers on a type parameter declaration are specified in 9.7.4 and 9.7.5.

In an interface's type parameter section, a type variable T directly depends on a type variable S if S is the bound of T, while T depends on S if either T directly depends on S or T directly depends on a type variable U that depends on S (using this definition recursively). It is a compile-time error if a type variable in a interface's type parameter section depends on itself.

The scope and shadowing of an interface's type parameter is specified in 6.3 and 6.4.

It is a compile-time error to refer to a type parameter of a generic interface I anywhere in the declaration of a static member of I (9.3, 9.4, 9.5).

References to an interface's type parameter from a static context or a nested class are restricted, as specified in 6.5.5.1.

A generic interface declaration defines a set of parameterized types (4.5), one for each possible parameterization of the type parameter section by type arguments. All of these parameterized types share the same interface at run time.

9.4 Method Declarations

InterfaceMethodDeclaration:
{InterfaceMethodModifier} MethodHeader MethodBody
InterfaceMethodModifier:
(one of)
Annotation public private
abstract default static strictfp

The following productions from 8.4, 8.4.5, and 8.4.7 are shown here for convenience:

MethodHeader:
Result MethodDeclarator [Throws]
TypeParameters {Annotation} Result MethodDeclarator [Throws]
Result:
UnannType
void
MethodDeclarator:
Identifier ( [ReceiverParameter ,] [FormalParameterList] ) [Dims]
MethodBody:
Block
;

The rules for annotation modifiers on an interface method declaration are specified in 9.7.4 and 9.7.5.

A method in the body of an interface may be declared public or private (6.6). If no access modifier is given, the method is implicitly public. It is permitted, but discouraged as a matter of style, to redundantly specify the public modifier for a method declaration in an interface.

A default method is an instance method declared in an interface with the default modifier. Its body is always represented by a block, which provides a default implementation for any class that implements the interface without overriding the method. Default methods are distinct from concrete methods (8.4.3.1), which are declared in classes, and from private interface methods, which are neither inherited nor overridden.

An interface can declare static methods, which are invoked without reference to a particular object. static interface methods are distinct from default, abstract, and private methods, which are instance methods.

It is a compile-time error to use the name of a type parameter of any surrounding declaration in the header or body of a static method of an interface.

This restriction is now specified in 6.5.5.1, directly at the point where the reference occurs.

A static method declaration introduces a static context (8.1.3).

References to an instance method from a static context or a nested class are restricted, as specified in 15.12.3.

The effect of the strictfp modifier is to make all float or double expressions within the body of a default or static method be explicitly FP-strict (15.4).

An interface method lacking a private, default, or static modifier is implicitly abstract. Its body is represented by a semicolon, not a block. It is permitted, but discouraged as a matter of style, to redundantly specify the abstract modifier for such a method declaration.

Note that an interface method may not be declared with protected or package access, or with the modifiers final, synchronized, or native.

It is a compile-time error if the same keyword appears more than once as a modifier for an interface method declaration, or if an interface method declaration has more than one of the access modifiers public and private (6.6).

It is a compile-time error if an interface method declaration has more than one of the keywords abstract, default, or static.

It is a compile-time error if an interface method declaration that contains the keyword private also contains the keyword abstract or default. It is permitted for an interface method declaration to contain both private and static.

It is a compile-time error if an interface method declaration that contains the keyword abstract also contains the keyword strictfp.

It is a compile-time error for the body of an interface to declare, explicitly or implicitly, two methods with override-equivalent signatures (8.4.2). However, an interface may inherit several abstract methods with such signatures (9.4.1).

A method declared in an interface may be generic. The rules for type parameters of a generic method in an interface are the same as for a generic method in a class (8.4.4).

9.4.3 Interface Method Body

A default method has a block body. This block of code provides an implementation of the method in the event that a class implements the interface but does not provide its own implementation of the method.

A private or static method also has a block body, which provides the implementation of the method.

It is a compile-time error if an interface method declaration is abstract (explicitly or implicitly) and has a block for its body.

It is a compile-time error if an interface method declaration is default, private, or static, and has a semicolon for its body.

It is a compile-time error for the body of a static method to attempt to reference the current object using the keyword this or the keyword super.

This restriction is specified in 15.8.3, 15.11.2, and 15.12.3, directly at the point where the reference occurs.

The rules for return statements in a method body are specified in 14.17.

If a method is declared to have a return type (8.4.5), then a compile-time error occurs if the body of the method can complete normally (14.1).

9.6 Annotation Interfaces

An annotation declaration specifies a new annotation interface, a special kind of interface. To distinguish an annotation declaration from a normal interface declaration, the keyword interface is preceded by an at sign (@).

AnnotationDeclaration:
{InterfaceModifier} @ interface TypeIdentifier AnnotationInterfaceBody

Note that the at sign (@) and the keyword interface are distinct tokens. It is possible to separate them with whitespace, but this is discouraged as a matter of style.

It is a compile-time error if an annotation declaration is nested within the body of a local class or interface declaration (14.3), or within the body of an anonymous class (15.9.5).

This rule ensures that an annotation interface always has a fully qualified name (6.7).

The rules for annotation modifiers on an annotation declaration are specified in 9.7.4 and 9.7.5.

The TypeIdentifier in an annotation declaration specifies the name of the annotation interface.

It is a compile-time error if an annotation interface has the same simple name as any of its enclosing classes or interfaces.

The direct superinterface of every annotation interface is java.lang.annotation.Annotation ([9.1.3]).

By virtue of the AnnotationInterfaceDeclaration syntax, an annotation interface declaration cannot be generic, and no extends clause is permitted.

A consequence of the fact that an annotation interface cannot explicitly declare a superclass type or superinterface type is that a subinterface of an annotation interface is never itself an annotation interface. Similarly, java.lang.annotation.Annotation is not itself an annotation interface.

An annotation interface inherits several members from java.lang.annotation.Annotation, including the implicitly declared methods corresponding to the instance methods of Object, yet these methods do not define elements of the annotation interface (9.6.1).

Because these methods do not define elements of the annotation interface, it is illegal to use them in annotations of that type (9.7). Without this rule, we could not ensure that elements were of the types representable in annotations, or that accessor methods for them would be available.

Unless explicitly modified herein, all of the rules that apply to normal interface declarations apply to annotation declarations.

For example, annotation interfaces share the same namespace as normal classes and interfaces; and annotation declarations have the same scope and accessibility as interface declarations.

Chapter 13: Binary Compatibility

13.1 The Form of a Binary

Programs must be compiled either into the class file format specified by The Java Virtual Machine Specification, Java SE 14 Edition, or into a representation that can be mapped into that format by a class loader written in the Java programming language.

A class file corresponding to a class or interface declaration must have certain properties. A number of these properties are specifically chosen to support source code transformations that preserve binary compatibility. The required properties are:

  1. The class or interface must be named by its binary name, which must meet the following constraints:

    • The binary name of a top level class or interface (7.6) is its canonical name (6.7).

    • The binary name of a member class or interface (8.5, 9.5) consists of the binary name of its immediately enclosing class or interface, followed by $, followed by the simple name of the member.

    • The binary name of a local class or interface (14.3) consists of the binary name of its immediately enclosing class or interface, followed by $, followed by a non-empty sequence of digits, followed by the simple name of the local class.

    • The binary name of an anonymous class (15.9.5) consists of the binary name of its immediately enclosing class or interface, followed by $, followed by a non-empty sequence of digits.

    • The binary name of a type variable declared by a generic class or interface (8.1.2, 9.1.2) is the binary name of its immediately enclosing class or interface, followed by $, followed by the simple name of the type variable.

    • The binary name of a type variable declared by a generic method (8.4.4) is the binary name of the class or interface declaring the method, followed by $, followed by the descriptor of the method (JVMS §4.3.3), followed by $, followed by the simple name of the type variable.

    • The binary name of a type variable declared by a generic constructor (8.8.4) is the binary name of the class declaring the constructor, followed by $, followed by the descriptor of the constructor (JVMS §4.3.3), followed by $, followed by the simple name of the type variable.

...

Chapter 14: Blocks and Statements

The sequence of execution of a program is controlled by statements, which are executed for their effect and do not have values.

Some statements contain other statements as part of their structure; such other statements are substatements of the statement. We say that statement S immediately contains statement U if there is no statement T different from S and U such that S contains T and T contains U. In the same manner, some statements contain expressions (15) as part of their structure.

The first section of this chapter discusses the distinction between normal and abrupt completion of statements (14.1). Most of the remaining sections explain the various kinds of statements, describing in detail both their normal behavior and any special treatment of abrupt completion.

Blocks are explained first (14.2), followed by local class and interface declarations (14.3) and local variable declaration statements (14.4).

Next a grammatical maneuver that sidesteps the familiar "dangling else" problem (14.5) is explained.

The last section (14.22) of this chapter addresses the requirement that every statement be reachable in a certain technical sense.

14.2 Blocks

A block is a sequence of statements, local class and interface declarations, and local variable declaration statements within braces.

Block:
{ [BlockStatements] }
BlockStatements:
BlockStatement {BlockStatement}
BlockStatement:
LocalVariableDeclarationStatement
ClassDeclaration
LocalClassOrInterfaceDeclaration
Statement

A block is executed by executing each of the local variable declaration statements and other statements in order from first to last (left to right). If all of these block statements complete normally, then the block completes normally. If any of these block statements complete abruptly for any reason, then the block completes abruptly for the same reason.

14.3 Local Class and Interface Declarations

A local class or a local interface is a nested class or interface (8, 9) that is not a member of any class and that has a name (6.2, 6.7) whose declaration is immediately contained by a block (14.2).

LocalClassOrInterfaceDeclaration:
ClassDeclaration
NormalInterfaceDeclaration

A local class may be an enum class (8.9). A local interface may not be an annotation interface (9.6).

All local classes are inner classes (8.1.3).

Local enum classes and local interfaces are implicitly static (8.1.1.4, 9.1.1.3). A local class that is not implicitly static is an inner class (8.1.3).

A local class or interface is not a member of any package, class, or interface. Unlike an anonymous class (15.9.5), a local class or interface has a simple name (6.2, 6.7).

Every local class declaration statement is immediately contained by a block (14.2). Local class type declaration statements and interface declarations may be intermixed freely with other kinds of statements in the enclosing block.

It is a compile-time error if a local class or interface declaration contains is declared with any of the access modifiers public, protected, or private (6.6), or the modifier static (8.1.1).

The scope and shadowing of a local class or interface declaration is specified in 6.3 and 6.4.

Example 14.3-1. Local Class and Interface Declarations

Here is an example that illustrates several aspects of the rules given above:

class Global {
    class Cyclic {}

    void foo() {
        new Cyclic(); // create a Global.Cyclic
        class Cyclic extends Cyclic {} // circular definition

        {
            class Local {}
            {
                class Local {} // compile-time error
            }
            class Local {} // compile-time error
            class AnotherLocal {
                void bar() {
                    class Local {} // ok
                }
            }
        }
        class Local {} // ok, not in scope of prior Local
    }
}

The first statement of method foo creates an instance of the member class Global.Cyclic rather than an instance of the local class Cyclic, because the statement appears prior to the scope of the local class declaration.

The fact that the scope of a local class declaration encompasses its whole declaration (not only its body) means that the definition of the local class Cyclic is indeed cyclic because it extends itself rather than Global.Cyclic. Consequently, the declaration of the local class Cyclic is rejected at compile time.

Since local class names cannot be redeclared within the same method (or constructor or initializer, as the case may be), the second and third declarations of Local result in compile-time errors. However, Local can be redeclared in the context of another, more deeply nested, class such as AnotherLocal.

The final declaration of Local is legal, since it occurs outside the scope of any prior declaration of Local.

14.4 Local Variable Declaration Statements

14.4.1 Local Variable Declarators and Types

Each declarator in a local variable declaration declares one local variable, whose name is the Identifier that appears in the declarator.

If the optional keyword final appears at the start of the declaration, the variable being declared is a final variable (4.12.4).

The declared type of a local variable is determined as follows:

Example 14.4.1-1. Type of Local Variables Declared With var

The following code illustrates the typing of variables declared with var:

var a = 1;                // a has type 'int'
var b = java.util.List.of(1, 2);  // b has type 'List<Integer>'
var c = "x".getClass();   // c has type 'Class<? extends String>'
                          // (see JLS 15.12.2.6)
var d = new Object() {};  // d has the type of the anonymous class
var e = (CharSequence & Comparable<String>) "x";
                          // e has type CharSequence & Comparable<String>
var f = () -> "hello";    // Illegal: lambda not in an assignment context
var g = null;             // Illegal: null type

Note that some variables declared with var cannot be declared with an explicit type, because the type of the variable is not denotable.

Upward projection is applied to the type of the initializer when determining the type of the variable. If the type of the initializer contains capture variables, this projection maps the type of the initializer to a supertype that does not contain capture variables.

While it would be possible to allow the type of the variable to mention capture variables, by projecting them away we enforce an attractive invariant that the scope of a capture variable is never larger than the statement containing the expression whose type is captured. Informally, capture variables cannot "leak" into subsequent statements.

A local variable of type float always contains a value that is an element of the float value set (4.2.3); similarly, a local variable of type double always contains a value that is an element of the double value set. It is not permitted for a local variable of type float to contain an element of the float-extended-exponent value set that is not also an element of the float value set, nor for a local variable of type double to contain an element of the double-extended-exponent value set that is not also an element of the double value set.

The scope and shadowing of a local variable declaration is specified in 6.3 and 6.4.

References to a local variable from a nested class or a lambda expression are restricted, as specified in 6.5.6.1.

14.14 The for Statement

14.14.1 The basic for Statement

The basic for statement executes some initialization code, then executes an Expression, a Statement, and some update code repeatedly until the value of the Expression is false.

BasicForStatement:
for ( [ForInit] ; [Expression] ; [ForUpdate] ) Statement
BasicForStatementNoShortIf:
for ( [ForInit] ; [Expression] ; [ForUpdate] ) StatementNoShortIf
ForInit:
StatementExpressionList
LocalVariableDeclaration
ForUpdate:
StatementExpressionList
StatementExpressionList:
StatementExpression {, StatementExpression}

The Expression must have type boolean or Boolean, or a compile-time error occurs.

The scope and shadowing of a local variable declared in the ForInit part of a basic for statement is specified in 6.3 and 6.4.

References to a local variable from a nested class or a lambda expression are restricted, as specified in 6.5.6.1.

14.14.2 The enhanced for statement

The enhanced for statement has the form:

EnhancedForStatement:
for ( {VariableModifier} LocalVariableType VariableDeclaratorId
: Expression )
Statement
EnhancedForStatementNoShortIf:
for ( {VariableModifier} LocalVariableType VariableDeclaratorId
: Expression )
StatementNoShortIf

The following productions from 4.3, 8.3, 8.4.1, and 14.4 are shown here for convenience:

VariableModifier:
Annotation
final
LocalVariableType:
UnannType
var
VariableDeclaratorId:
Identifier [Dims]
Dims:
{Annotation} [ ] {{Annotation} [ ]}

The header of the enhanced for statement declares a local variable, whose name is the identifier given by VariableDeclaratorId.

If the keyword final appears at the start of the declaration, the variable being declared is a final variable (4.12.4).

It is a compile-time error if the LocalVariableType is var and the VariableDeclaratorId has one or more bracket pairs.

The type of the Expression must be a subtype of the raw type Iterable or an array type (10.1), or a compile-time error occurs.

The type of the local variable is determined as follows:

The scope and shadowing of the local variable is specified in 6.3 and 6.4.

References to the local variable from a nested class or a lambda expression are restricted, as specified in 6.5.6.1.

When an enhanced for statement is executed, the local variable is initialized, on each iteration of the loop, to successive elements of the array or Iterable produced by the expression. The precise meaning of the enhanced for statement is given by translation into a basic for statement, as follows:

...

14.20 The try statement

A try statement executes a block. If a value is thrown and the try statement has one or more catch clauses that can catch it, then control will be transferred to the first such catch clause. If the try statement has a finally clause, then another block of code is executed, no matter whether the try block completes normally or abruptly, and no matter whether a catch clause is first given control.

TryStatement:
try Block Catches
try Block [Catches] Finally
TryWithResourcesStatement
Catches:
CatchClause {CatchClause}
CatchClause:
catch ( CatchFormalParameter ) Block
CatchFormalParameter:
{VariableModifier} CatchType VariableDeclaratorId
CatchType:
UnannClassType {| ClassType}
Finally:
finally Block

See 8.3 for UnannClassType. The following productions from 4.3, 8.3, and 8.4.1 are shown here for convenience:

VariableModifier:
Annotation
final
VariableDeclaratorId:
Identifier [Dims]
Dims:
{Annotation} [ ] {{Annotation} [ ]}

The Block immediately after the keyword try is called the try block of the try statement.

The Block immediately after the keyword finally is called the finally block of the try statement.

A try statement may have catch clauses, also called exception handlers.

A catch clause declares exactly one parameter, which is called an exception parameter.

It is a compile-time error if final appears more than once as a modifier for an exception parameter declaration.

The scope and shadowing of an exception parameter is specified in 6.3 and 6.4.

References to an exception parameter from a nested class or a lambda expression are restricted, as specified in 6.5.6.1.

An exception parameter may denote its type as either a single class type or a union of two or more class types (called alternatives). The alternatives of a union are syntactically separated by |.

A catch clause whose exception parameter is denoted as a single class type is called a uni-catch clause.

A catch clause whose exception parameter is denoted as a union of types is called a multi-catch clause.

...

14.20.3 try-with-resources

A try-with-resources statement is parameterized with local variables (known as resources) that are initialized before execution of the try block and closed automatically, in the reverse order from which they were initialized, after execution of the try block. catch clauses and a finally clause are often unnecessary when resources are closed automatically.

TryWithResourcesStatement:
try ResourceSpecification Block [Catches] [Finally]
ResourceSpecification:
( ResourceList [;] )
ResourceList:
Resource {; Resource}
Resource:
{VariableModifier} LocalVariableType Identifier = Expression
VariableAccess
VariableAccess:
ExpressionName
FieldAccess

The following productions from 8.4.1 and 14.4 are shown here for convenience:

VariableModifier:
Annotation
final
LocalVariableType:
UnannType
var

A resource specification uses variables to denote resources for the try statement, either by declaring local variables with initializer expressions or by referring to suitable existing variables. An existing variable is referred to by either an expression name (6.5.6) or a field access expression (15.11).

It is a compile-time error for a resource specification to declare two variables with the same name.

It is a compile-time error if final appears more than once as a modifier for each variable declared in a resource specification.

A variable declared in a resource specification is implicitly declared final if it is not explicitly declared final (4.12.4).

A resource denoted by an expression name or field access expression must be a final or effectively final variable that is definitely assigned before the try-with-resources statement (16), or a compile-time error occurs.

It is a compile-time error if the LocalVariableType of a variable declared in a resource specification is var and the initializer expression contains a reference to the variable.

The type of a variable declared in a resource specification is determined as follows:

The type of a variable declared or referred to as a resource in a resource specification must be a subtype of AutoCloseable, or a compile-time error occurs.

The scope and shadowing of a variable declared in a resource specification is specified in 6.3 and 6.4.

References to a variable declared in a resource specification from a nested class are restricted, as specified in 6.5.6.1.

Resources are initialized in left-to-right order. If a resource fails to initialize (that is, its initializer expression throws an exception), then all resources initialized so far by the try-with-resources statement are closed. If all resources initialize successfully, the try block executes as normal and then all non-null resources of the try-with-resources statement are closed.

Resources are closed in the reverse order from that in which they were initialized. A resource is closed only if it initialized to a non-null value. An exception from the closing of one resource does not prevent the closing of other resources. Such an exception is suppressed if an exception was thrown previously by an initializer, the try block, or the closing of a resource.

A try-with-resources statement whose resource specification indicates multiple resources is treated as if it were multiple try-with-resources statements, each of which has a resource specification that indicates a single resource. When a try-with-resources statement with n resources (n > 1) is translated, the result is a try-with-resources statement with n-1 resources. After n such translations, there are n nested try-catch-finally statements, and the overall translation is complete.

Chapter 15: Expressions

15.8 Primary Expressions

15.8.3 this

The keyword this may be used only in the following contexts:

If it appears anywhere else, a compile-time error occurs.

The keyword this may be used in a lambda expression only if it is allowed in the context in which the lambda expression appears. Otherwise, a compile-time error occurs.

When used as a primary expression, the keyword this denotes a value that is a reference to the object for which the instance method or default method was invoked (15.12), or to the object being constructed. The value denoted by this in a lambda body 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).

Let C be 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.

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

There's no need to enumerate all the places where this can be used when we've already defined the concept of static context.

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

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 is the n'th lexically enclosing class or interface declaration of the class or interface in which immediately enclosing the qualified this expression appears.

The value of an expression of the form TypeName.this is the n'th lexically enclosing instance of this.

If TypeName is generic, with type parameters F1, ..., Fn, the type of the expression is TypeName<F1, ..., Fn>. Otherwise, the type of the expression is TypeName.

It is a compile-time error if TypeName is not a lexically enclosing class or interface declaration of the expression, or if the expression occurs in a class or interface which is not an inner class of TypeName or TypeName itself.

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 the immediately enclosing class or interface declaration of 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 are valid only in an instance method, instance initializer, or constructor of a class, or in the initializer of an instance variable of a class. If they appear anywhere else, a compile-time error occurs.

These are exactly the same situations in which the keyword this may be used in a class declaration (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).

It is a compile-time error if the forms using the keyword super appear in the declaration of class Object, since Object has no superclass.

It is a compile-time error if the immediately enclosing class or interface declaration of a field access expression of the form super.Identifier is the class Object or an interface.

Let U be the immediately enclosing class or interface declaration of a field access expression of the form T.super.Identifier. It is a compile-time error if U is not an inner class of T or T itself. It is a compile-time error if T is the class Object or an interface.

The restriction on interfaces was not specified previously, although it became relevant when default methods were introduced. Compare 15.12.1.

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.

It is a compile-time error if the current class is not an inner class of class T or T itself.

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.1 Compile-Time Step 1: Determine Type to Search

The first step in processing a method invocation at compile time is to figure out the name of the method to be invoked and which type to search for definitions of methods of that name.

The name of the method is specified by the MethodName or Identifier which immediately precedes the left parenthesis of the MethodInvocation.

For the type to search, there are six cases to consider, depending on the form that precedes the left parenthesis of the MethodInvocation:

The TypeName . super syntax is overloaded: traditionally, the TypeName refers to a lexically enclosing class declaration, and the target is the superclass of this class, as if the invocation were an unqualified super in the lexically enclosing class declaration.

class Superclass {
    void foo() { System.out.println("Hi"); }
}

class Subclass1 extends Superclass {
    void foo() { throw new UnsupportedOperationException(); }

    Runnable tweak = new Runnable() {
        void run() {
            Subclass1.super.foo();  // Gets the 'println' behavior
        }
    };
}

To support invocation of default methods in superinterfaces, the TypeName may also refer to a direct superinterface of the current class or interface, and the target is that superinterface.

interface Superinterface {
    default void foo() { System.out.println("Hi"); }
}

class Subclass2 implements Superinterface {
    void foo() { throw new UnsupportedOperationException(); }

    void tweak() {
        Superinterface.super.foo();  // Gets the 'println' behavior
    }
}

No syntax supports a combination of these forms, that is, invoking a superinterface method of a lexically enclosing class declaration, as if the invocation were of the form InterfaceName . super in the lexically enclosing class declaration.

class Subclass3 implements Superinterface {
    void foo() { throw new UnsupportedOperationException(); }

    Runnable tweak = new Runnable() {
        void run() {
            Subclass3.Superinterface.super.foo();  // Illegal
        }
    };
}

A workaround is to introduce a private method in the lexically enclosing class declaration that performs the interface super call.

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

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.

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

Compare 15.12.3.

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 (8.4.8, 9.4.1) the compile-time declaration from a direct superclass or direct superinterface of the type whose declaration immediately encloses the method reference expression.

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.

15.27 Lambda Expressions

15.27.1 Lambda Parameters

The formal parameters of a lambda expression, if any, are specified by either a parenthesized list of comma-separated parameter specifiers or a parenthesized list of comma-separated identifiers. In a list of parameter specifiers, each parameter specifier consists of optional modifiers, then a type (or var), then an identifier that specifies the name of the parameter. In a list of identifiers, each identifier specifies the name of the parameter.

If a lambda expression has no formal parameters, then an empty pair of parentheses appears before the -> and the lambda body.

If a lambda expression has exactly one formal parameter, and the parameter is specified by an identifier instead of a parameter specifier, then the parentheses around the identifier may be elided.

LambdaParameters:
( [LambdaParameterList] )
Identifier
LambdaParameterList:
LambdaParameter {, LambdaParameter}
Identifier {, Identifier}
LambdaParameter:
{VariableModifier} LambdaParameterType VariableDeclaratorId
VariableArityParameter
LambdaParameterType:
UnannType
var

The following productions from 8.4.1, 8.3, and 4.3 are shown here for convenience:

VariableArityParameter:
{VariableModifier} UnannType {Annotation} ... Identifier
VariableModifier:
Annotation
final
VariableDeclaratorId:
Identifier [Dims]
Dims:
{Annotation} [ ] {{Annotation} [ ]}

A formal parameter of a lambda expression may be declared final, or annotated, only if specified by a parameter specifier. If a formal parameter is specified by an identifier instead, then the formal parameter is not final and has no annotations.

A formal parameter of a lambda expression may be a variable arity parameter, indicated by an ellipsis following the type in a parameter specifier. At most one variable arity parameter is permitted for a lambda expression. It is a compile-time error if a variable arity parameter appears anywhere in the list of parameter specifiers except the last position.

Each formal parameter of a lambda expression has either an inferred type or a declared type:

No distinction is made between the following lambda parameter lists:

(int... x) `->` BODY
(int[] x) `->` BODY

Either can be used, whether the functional interface's abstract method is fixed arity or variable arity. (This is consistent with the rules for method overriding.) Since lambda expressions are never directly invoked, using int... for the formal parameter where the functional interface uses int[] can have no impact on the surrounding program. In a lambda body, a variable arity parameter is treated just like an array-typed parameter.

A lambda expression where all the formal parameters have declared types is said to be explicitly typed. A lambda expression where all the formal parameters have inferred types is said to be implicitly typed. A lambda expression with no formal parameters is explicitly typed.

If a lambda expression is implicitly typed, then its lambda body is interpreted according to the context in which it appears. Specifically, the types of expressions in the body, and the checked exceptions thrown by the body, and the type correctness of code in the body all depend on the types inferred for the formal parameters. This implies that inference of formal parameter types must occur "before" attempting to type-check the lambda body.

It is a compile-time error if a lambda expression declares a formal parameter with a declared type and a formal parameter with an inferred type.

This rule prevents a mix of inferred and declared types in the formal parameters, such as (x, int y) -> BODY or (var x, int y) -> BODY. Note that if all the formal parameters have inferred types, the grammar prevents a mix of identifiers and var parameter specifiers, such as (x, var y) -> BODY or (var x, y) -> BODY.

The rules for annotation modifiers on a formal parameter declaration are specified in 9.7.4 and 9.7.5.

It is a compile-time error if final appears more than once as a modifier for a formal parameter declaration.

It is a compile-time error if the LambdaParameterType of a formal parameter is var and the VariableDeclaratorId of the same formal parameter has one or more bracket pairs.

The scope and shadowing of a formal parameter declaration is specified in 6.3 and 6.4.

References to a formal parameter from a nested class or a nested lambda expression are restricted, as specified in 6.5.6.1.

It is a compile-time error for a lambda expression to declare two formal parameters with the same name. (That is, their declarations mention the same Identifier.)

In Java SE 8, the use of _ as the name of a lambda parameter was forbidden, and its use discouraged as the name for other kinds of variable (4.12.3). As of Java SE 9, _ is a keyword (3.9) so it cannot be used as a variable name in any context.

It is a compile-time error if a formal parameter that is declared final is assigned to within the body of the lambda expression.

When the lambda expression is invoked (via a method invocation expression (15.12)), the values of the actual argument expressions initialize newly created parameter variables, each of the declared or inferred type, before execution of the lambda body. The Identifier that appears in the LambdaParameter or directly in the LambdaParameterList or LambdaParameters may be used as a simple name in the lambda body to refer to the formal parameter.

A lambda expression's formal parameter of type float always contains an element of the float value set (4.2.3); similarly, a lambda expression's formal parameter of type double always contains an element of the double value set. It is not permitted for a lambda expression's formal parameter of type float to contain an element of the float-extended-exponent value set that is not also an element of the float value set, nor for a lambda expression's formal parameter of type double to contain an element of the double-extended-exponent value set that is not also an element of the double value set.

15.27.2 Lambda Body

A lambda body is either a single expression or a block (14.2). Like a method body, a lambda body describes code that will be executed whenever an invocation occurs.

LambdaBody:
Expression
Block

Unlike code appearing in anonymous class declarations, the meaning of names and the this and super keywords appearing in a lambda body, along with the accessibility of referenced declarations, are the same as in the surrounding context (except that lambda parameters introduce new names).

The transparency of this (both explicit and implicit) in the body of a lambda expression - that is, treating it the same as in the surrounding context - allows more flexibility for implementations, and prevents the meaning of unqualified names in the body from being dependent on overload resolution.

Practically speaking, it is unusual for a lambda expression to need to talk about itself (either to call itself recursively or to invoke its other methods), while it is more common to want to use names to refer to things in the enclosing class that would otherwise be shadowed (this, toString()). If it is necessary for a lambda expression to refer to itself (as if via this), a method reference or an anonymous inner class should be used instead.

A block lambda body is void-compatible if every return statement in the block has the form return;.

A block lambda body is value-compatible if it cannot complete normally (14.21) and every return statement in the block has the form return Expression;.

It is a compile-time error if a block lambda body is neither void-compatible nor value-compatible.

In a value-compatible block lambda body, the result expressions are any expressions that may produce an invocation's value. Specifically, for each statement of the form return Expression ; contained by the body, the Expression is a result expression.

The following lambda bodies are void-compatible:

() `->` {}
() `->` { System.out.println("done"); }

These are value-compatible:

() `->` { return "done"; }
() `->` { if (...) return 1; else return 0; }

These are both:

() `->` { throw new RuntimeException(); }
() `->` { while (true); }

This is neither:

() `->` { if (...) return "done"; System.out.println("done"); }

The handling of void/value-compatible and the meaning of names in the body jointly serve to minimize the dependency on a particular target type in the given context, which is useful both for implementations and for programmer comprehension. While expressions can be assigned different types during overload resolution depending on the target type, the meaning of unqualified names and the basic structure of the lambda body do not change.

Note that the void/value-compatible definition is not a strictly structural property: "can complete normally" depends on the values of constant expressions, and these may include names that reference constant variables.

Any local variable, formal parameter, or exception parameter used but not declared in a lambda expression must either be declared final or be effectively final (4.12.4), or a compile-time error occurs where the use is attempted.

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

Similar rules on variable use apply in the body of an inner class (8.1.3). The restriction to effectively final variables prohibits access to dynamically-changing local variables, whose capture would likely introduce concurrency problems. Compared to the final restriction, it reduces the clerical burden on programmers.

The restriction to effectively final variables includes standard loop variables, but not enhanced-for loop variables, which are treated as distinct for each iteration of the loop (14.14.2).

The following lambda bodies demonstrate use of effectively final variables.

void m1(int x) {
    int y = 1;
    foo(() -> x+y);
    // Legal: x and y are both effectively final.
}

void m2(int x) {
    int y;
    y = 1;
    foo(() -> x+y);
    // Legal: x and y are both effectively final.
}

void m3(int x) {
    int y;
    if (...) y = 1;
    foo(() -> x+y);
    // Illegal: y is effectively final, but not definitely assigned.
}

void m4(int x) {
    int y;
    if (...) y = 1; else y = 2;
    foo(() -> x+y);
    // Legal: x and y are both effectively final.
}
void m5(int x) {
    int y;
    if (...) y = 1;
    y = 2;
    foo(() -> x+y);
    // Illegal: y is not effectively final.
}

void m6(int x) {
    foo(() -> x+1);
    x++;
    // Illegal: x is not effectively final.
}

void m7(int x) {
    foo(() -> x=1);
    // Illegal: x is not effectively final.
}

void m8() {
    int y;
    foo(() -> y=1);
    // Illegal: y is not definitely assigned before the lambda.
}

void m9(String[] arr) {
    for (String s : arr) {
        foo(() -> s);
        // Legal: s is effectively final
        // (it is a new variable on each iteration)
    }
}

void m10(String[] arr) {
    for (int i = 0; i < arr.length; i++) {
        foo(() -> arr[i]);
        // Illegal: i is not effectively final
        // (it is not final, and is incremented)
    }
}

Some of this may be useful to move to 6.5.6.1.

Chapter 16: Definite Assignment

16.2 Definite Assignment and Statements

16.2.3 Local Class Declaration Statements and Interface Declarations