This document describes changes to the Java Language Specification as modified by Consistent Class and Interface Terminology to support local interface and enum class declarations. Such classes and interfaces are implicitly static
, with no access to enclosing instances, local variables, and type parameters. These enhancements are part of the Records feature. See the JEP Draft for additional details.
There are two significant groups of changes:
Expanding the syntax for local class declarations (14.3).
Adjusting 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 these new contexts.These changes are general enough to support other forms of nested statics, should the language be enhanced to support them in the future.
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:
A module, declared in a
module
declaration (7.7)A package, declared in a
package
declaration (7.4)An imported class or interface, declared in a single-type-import declaration
An imported
static
member, declared in a single-static-import declaration or a static-import-on-demand declaration (7.5.3, 7.5.4)A class, declared in a class declaration (8.1) or an enum declaration (8.9)
An interface, declared in an interface declaration (9.1) or an annotation declaration (9.6)
A type parameter, declared as part of the declaration of a generic class, interface, method, or constructor (8.1.2, 9.1.2, 8.4.4, 8.8.4)
A member of a reference type (8.2, 9.2, 8.9.3, 9.6, 10.7), one of the following:
A field, one of the following:
A method, one of the following:
An enum constant (8.9.1)
A parameter, one of the following:
A formal parameter of a method or constructor of a class type or enum type (8.4.1, 8.8.1, 8.9.2), or of a lambda expression (15.27.1)
A formal parameter of an
abstract
method of an interface type or annotation type (9.4, 9.6.1)An exception parameter of an exception handler declared in a
catch
clause of atry
statement (14.20)
A local variable, one of the following:
A local class or interface (14.3), declared in one of the following:
A class declaration
An enum declaration
An interface declaration
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:
Its own initializer
Any further declarators to the right in the ForInit part of the
for
statementThe Expression and ForUpdate parts of the
for
statementThe contained Statement
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,
orlocal class, or local interface in nested class or interface declarations that occur in the scope of the variable,orlocal class, or local interface; such nested class or interface declarations may be localclassesclass or interface declarations (14.3) or anonymousclassesclass declarations (15.915.9.5). Thus, the declaration of a formal parameter, local variable,orlocal 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 thecatch
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:
If a field name obscures a package name, then an
import
declaration (7.5) can usually be used to make available the type names declared in that package.If a field name obscures a type name, then a fully qualified name for the type can be used unless the type name denotes a local class or interface (14.3).
Field names cannot obscure method names.
If a field name is shadowed by a declaration of a parameter or local variable, then the name of the parameter or local variable can be changed without affecting other code.
...
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 Identifier appears within the scope (6.3) of a
local variable declaration (14.4) or parameter declaration (8.4.1, 8.8.1, 14.20) or field declaration (8.3)variable with that name, then the AmbiguousName is reclassified as an ExpressionName.Otherwise, if a field of that name is declared in the compilation unit (7.3) containing the Identifier by a single-static-import declaration (7.5.3), or by a static-import-on-demand declaration (7.5.4) then the AmbiguousName is reclassified as an ExpressionName.An imported field is specified to be in scope. It doesn't require an additional rule.
Otherwise, if the Identifier is a valid TypeIdentifier and appears within the scope (6.3) of a
top level class (8) or interface type declaration (9), a local class declaration (14.3) or member type declaration (8.5, 9.5)class, interface, or type parameter with that name, then the AmbiguousName is reclassified as a TypeName.Bug fix: this rule was never updated to account for type parameters.
Otherwise, if the Identifier is a valid TypeIdentifier and a type of that name is declared in the compilation unit (7.3) containing the Identifier, either by a single-type-import declaration (7.5.1), or by a type-import-on-demand declaration (7.5.2), or by a single-static-import declaration (7.5.3), or by a static-import-on-demand declaration (7.5.4), then the AmbiguousName is reclassified as a TypeName.An imported type is specified to be in scope. It doesn't require an additional rule.
Otherwise, the AmbiguousName is reclassified as a PackageName. A later step determines whether or not a package of that name actually exists.
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:
If the name to the left of the "
.
" is reclassified as a PackageName, then:If the Identifier is a valid TypeIdentifier, and there is a package whose name is the name to the left of the "
.
", and that package contains a declaration of a type whose name is the same as the Identifier, then this AmbiguousName is reclassified as a TypeName.Otherwise, this AmbiguousName is reclassified as a PackageName. A later step determines whether or not a package of that name actually exists.
If the name to the left of the "
.
" is reclassified as a TypeName, then:If the Identifier is the name of a method or field of the type denoted by TypeName, then this AmbiguousName is reclassified as an ExpressionName.
Otherwise, if the Identifier is a valid TypeIdentifier and is the name of a member type of the type denoted by TypeName, then this AmbiguousName is reclassified as a TypeName.
Otherwise, a compile-time error occurs.
If the name to the left of the "
.
" is reclassified as an ExpressionName, then this AmbiguousName is reclassified as an ExpressionName. A later step determines whether or not a member with the name Identifier actually exists.
The requirement that a potential type name be "a valid TypeIdentifier" prevents treating
var
andyield
as a type name. It is usually redundant, because the rules for declarations already prevent the introduction of types namedvar
andyield
. However, in some cases, a compiler may find a binary class namedvar
oryield
, 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:
The simple name
org
is reclassified as a PackageName (since there is no variable or type named org in scope).Next, assuming that there is no class or interface named
rpgpoet
in any compilation unit of packageorg
(and we know that there is no such class or interface because packageorg
has a subpackage namedrpgpoet
), the qualified nameorg.rpgpoet
is reclassified as a PackageName.Next, because package
org.rpgpoet
has an accessible (6.6) interface type namedMusic
, the qualified nameorg.rpgpoet.Music
is reclassified as a TypeName.Finally, because the name
org.rpgpoet.Music
is a TypeName, the qualified nameorg.rpgpoet.Music.wizards
is reclassified as an ExpressionName.
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:
The type name must not appear in a static context (8.1.3), and
If the type name appears in a nested class or interface declaration in C, then the immediately enclosing class or interface declaration of the type name must specify an inner class (8.1.3) of C.
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:
The type name must not appear in a static context except by virtue of being enclosed by m, and
If the type name appears in a nested class or interface declaration of C, then the immediately enclosing class or interface declaration of the type name must specify an inner class (8.1.3) declared in the body of m, or an inner class of an inner class declared in the body of m.
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 then:static
type declaration, class method, class variable initializer, or static initializer (8.7),
The expression name must not appear in a static context (8.1.3), and
If the expression name appears in a nested class or interface declaration in C, then the immediately enclosing class or interface declaration of the expression name must be an inner class (8.1.3) of C.
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:
The expression name must not appear in a static context except by virtue of being enclosed by X, and
If the expression name appears in a nested class or interface declaration in C, then the immediately enclosing class or interface declaration of the expression name must be an inner class (8.1.3) declared in X, or an inner class of an inner class declared in X.
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 Checker1 {
void checkWhitespace(int x) {
String arg = args[x];
if (!arg.trim().equals(arg)) {
throw new IllegalArgumentException();
}
}
}
interface I {
class Checker2 {
void checkFlag(int x) {
String arg = args[x]; // compile-time error
if (!arg.startsWith("-")) {
throw new IllegalArgumentException();
}
}
}
}
final Checker1 c1 = new Checker1();
I.Checker2 c2 = new I.Checker2();
for (int i = 1; i < args.length; i++) {
Runnable r = () -> {
c1.checkWhitespace(i); // compile-time error
c2.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 Checker1
. However, using the name args
in the instance method checkFlag
of nested class Checker2
is illegal, because Checker2
is not an inner class.
The local variables c1
and c2
are in scope for the last few lines of the body of method main
. Both can be used in the body of the lambda expression: c1
is declared final
, and c2
is effectively final.
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:
The fully qualified name of a primitive type is the keyword for that primitive type, namely
byte
,short
,char
,int
,long
,float
,double
, orboolean
.The fully qualified name of a named package that is not a subpackage of a named package is its simple name.
The fully qualified name of a named package that is a subpackage of another named package consists of the fully qualified name of the containing package, followed by "
.
", followed by the simple (member) name of the subpackage.The fully qualified name of a top level class or top level interface that is declared in an unnamed package is the simple name of the class or interface.
The fully qualified name of a top level class or top level interface that is declared in a named package consists of the fully qualified name of the package, followed by "
.
", followed by the simple name of the class or interface.
Each member class, member interface, and array type may have a fully qualified name:
A member class or member interface M of another class or interface C has a fully qualified name if and only if C has a fully qualified name.
In that case, the fully qualified name of M consists of the fully qualified name of C, followed by "
.
", followed by the simple name of M.An array type has a fully qualified name if and only if its element type has a fully qualified name.
In that case, the fully qualified name of an array type consists of the fully qualified name of the component type of the array type followed by "
[]
".
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:
- For every primitive type, named package, top level class, and top level interface, the canonical name is the same as the fully qualified name.
Each member class, member interface, and array type may have a canonical name:
A member class or member interface M declared in another class or interface C has a canonical name if and only if C has a canonical name.
In that case, the canonical name of M consists of the canonical name of C, followed by "
.
", followed by the simple name of M.An array type has a canonical name if and only if its component type has a canonical name.
In that case, the canonical name of the array type consists of the canonical name of the component type of the array type followed by "
[]
".
A local class, local interface, or anonymous class does not have a canonical name.
Example 6.7-1. Fully Qualified Names
The fully qualified name of the type
long
is "long
".The fully qualified name of the package
java.lang
is "java.lang
" because it is subpackagelang
of packagejava
.The fully qualified name of the class
Object
, which is defined in the packagejava.lang
, is "java.lang.Object
".The fully qualified name of the interface
Enumeration
, which is defined in the packagejava.util
, is "java.util.Enumeration
".The fully qualified name of the type "array of
double
" is "double[]
".The fully qualified name of the type "array of array of array of array of
String
" is "java.lang.String[][][][]
".
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), not to local classes (14.3).
The access modifiers protected
and private
pertain only to member classes within a directly enclosing class declaration.
The modifier static
pertains only to member classes and local classes, not to top level or local classes.
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
TypeVariableextends
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 valuetrue
.
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 Nested interfaces (9.1), nested enum classes (8.9), member annotation interfaces (9.6), and member classes of interfaces (9.5) are implicitly static
(9.5) so is never considered to be an inner class.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
}
These restrictions are no longer necessary, but remain in place for now. They can be relaxed separately as a new feature: static
members of 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 type.
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 or interface declaration. An instance variable is always defined with respect to an instance. In the case of instance variables of an enclosing class or interface declaration, the instance variable must be defined with respect to an enclosing instance of that class or interface. 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).
It is a compile-time error if a reference by simple name to any instance variable occurs in the initializer.
It is a compile-time error if the keyword
this
(15.8.3) or the keywordsuper
(15.11.2, 15.12) occurs in the 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 otherstatic
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).
Note that
static
fields that are constant variables (4.12.4) are initialized before otherstatic
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).
The initializer may refer by simple name to any class variable declared in or inherited by the class, even one whose declaration occurs to the right of the initializer (3.5).
The initializer may refer to the current object using the keyword
this
(15.8.3) or the keywordsuper
(15.11.2, 15.12).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 formal parameter is not a variable arity parameter, then the declared type is denoted by UnannType if no bracket pairs appear in UnannType and VariableDeclaratorId, and specified by 10.2 otherwise.
If the formal parameter is a variable arity parameter, then the declared type is an array type specified by 10.2.
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:
M and N have same number of type parameters (possibly zero).
Where A1, ..., An are the type parameters of M and B1, ..., Bn are the type parameters of N, let θ=[B1:=A1, ..., Bn:=An]. Then, for all i (1 ≤ i ≤ n), the bound of Ai is the same type as θ applied to the bound of Bi.
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:
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.In a class instance creation expression for
aan 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.In a class instance creation expression for an anonymous class, and where the anonymous class's superclass is
either inner or localan 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, whereasaan 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 ofaan 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:
Alternate constructor invocations begin with the keyword
this
(possibly prefaced with explicit type arguments). They are used to invoke an alternate constructor of the same class.Superclass constructor invocations begin with either the keyword
super
(possibly prefaced with explicit type arguments) or a Primary expression or an ExpressionName. They are used to invoke a constructor of the direct superclass. They are further divided:Unqualified superclass constructor invocations begin with the keyword
super
(possibly prefaced with explicit type arguments).Qualified superclass constructor invocations begin with a Primary expression or an ExpressionName. They allow a subclass constructor to explicitly specify the newly created object's immediately enclosing instance with respect to the direct superclass (8.1.3). This may be necessary when the superclass is an inner class.
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 S is an inner member class, but S is not a member of a class enclosing C, then a compile-time error occurs.
Otherwise, let O be the innermost enclosing class of C of which S is a member. C must be an inner class of O (8.1.3), or a compile-time error occurs.
If S is an inner local class, and S does not occur in a static context, let O be the immediately enclosing class or interface declaration of S. C must be an inner class of O (8.1.3), or a compile-time error occurs.
If a superclass constructor invocation statement is qualified, then:
If S is not an inner class, or if the declaration of S occurs in a static context, then a compile-time error occurs.
Otherwise, let p be the Primary expression or the ExpressionName immediately preceding "
.super
", and let O be the immediately enclosing class of S. It is a compile-time error if the type of p is not O or a subclass of O, or if the type of p is not accessible (6.6).
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:
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.
IfOtherwise, if the superclass constructor invocation is unqualified, then S is necessarilyaan inner local class or an inner member class.If S is
aan 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.IfOtherwise, 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
, aNullPointerException
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.
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.
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 [Superinterfaces] 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 declaration 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).
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:
The
final
clone
method inEnum
ensures that enum constants can never be cloned.Reflective instantiation of enum classes is prohibited.
Special treatment by the serialization mechanism ensures that duplicate instances are never created as a result of deserialization.
Chapter 9: Interfaces
An interface declaration introduces a new abstract reference type 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, 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 to every kind of interface declaration only to top level interfaces (7.6) and member interfaces (8.5, 9.5), not to local interfaces (14.3).
The access modifiers protected
and private
pertain only to member interfaces whose declarations are directly enclosed by a within a directly enclosing class declaration (8.5.1).
The modifier static
pertains only to member interfaces (8.5.1, 9.5) and local interfaces, not to top level interfaces (7.6).
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).
It is impossible to declare an interface as a member of an inner class (8.1.3), because an inner class cannot have
static
members except for constant variables.
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
TypeVariableextends
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 modifiersfinal
,synchronized
, ornative
.
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 keywordinterface
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).
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
.
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 or superinterface is that a subclass or 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:
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
- EnumDeclaration
- InterfaceDeclaration
An annotation interface (9.6) may not be declared as a local interface.
Local class and interface declarations may be intermixed freely with statements in the block.
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).
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).
Every local class declaration statement is immediately contained by a block (14.2). Local class type declaration statements may be intermixed freely with other kinds of statements in the 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:
If LocalVariableType is an UnannType, and no bracket pairs appear in UnannType or VariableDeclaratorId, then UnannType denotes the type of the local variable.
If LocalVariableType is an UnannType, and bracket pairs appear in UnannType or VariableDeclaratorId, then the type of the local variable is specified by 10.2.
If LocalVariableType is
var
, then let T be the type of the initializer expression when treated as if it did not appear in an assignment context, and were thus a standalone expression (15.2). The type of the local variable is the upward projection of T with respect to all synthetic type variables mentioned by T (4.10.5).It is a compile-time error if T is the null type.
Because the initializer is treated as if it did not appear in an assignment context, an error occurs if it is a lambda expression (15.27) or a method reference expression (15.13).
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:
If LocalVariableType is an UnannType, and no bracket pairs appear in UnannType or VariableDeclaratorId, then UnannType denotes the type of the local variable.
If LocalVariableType is an UnannType, and bracket pairs appear in UnannType or VariableDeclaratorId, then the type of the local variable is specified by 10.2.
If LocalVariableType is
var
, then let T be derived from the type of the Expression, as follows:If the Expression has an array type, then T is the component type of the array type.
Otherwise, if the Expression has a type that is a subtype of
Iterable<
X>
, for some type X, then T is X.Otherwise, the Expression has a type that is a subtype of the raw type
Iterable
, and T isObject
.
The type of the local variable is the upward projection of T with respect to all synthetic type variables mentioned by T (4.10.5).
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 Catchestry
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:
If LocalVariableType is an UnannType, then UnannType denotes the type of the local variable.
If LocalVariableType is
var
, then let T be the type of the initializer expression when treated as if it did not appear in an assignment context, and were thus a standalone expression (15.2). The type of the local variable is the upward projection of T with respect to all synthetic type variables mentioned by T (4.10.5).It is a compile-time error if T is the null type.
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:
in the body of an instance method or default method (8.4.7, 9.4.3)
in the body of a constructor of a class (8.8.7)
in an instance initializer of a class (8.6)
in the initializer of an instance variable of a class (8.3.2)
to denote a receiver parameter (8.4)
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).
The type of this
is the innermost class or interface type T within which the keyword this
occurs.
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.
Default methods provide the unique ability to access
this
inside an interface. (All other interface methods are eitherabstract
orstatic
, so provide no access tothis
.) As a result, it is possible forthis
to have an interface type.
Private interface methods make this no longer true. In modern Java, some interface methods have bodies. No need to treat this as a strange anomaly.
At run time, the class of the actual object referred to may be T, if T is a class type, or a class that is a subtype of T.
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 T be the type denoted by TypeName. Let n be an integer such that T 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
.
The type of the expression is T.
It is a compile-time error if the expression occurs in a class or interface which is not an inner class of class T or T 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 T or T 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, then:
If the class instance creation expression occurs in a static context, then i has no immediately enclosing instance.
Otherwise, the immediately enclosing instance of i is
this
.
If C is
aan inner local class, then:If C occurs in a static context, then i has no immediately enclosing instance.
Otherwise, if the class instance creation expression occurs in a static context, then a compile-time error occurs.
Otherwise, let O be the immediately enclosing class or interface declaration of C, and let U be the immediately enclosing class or interface declaration of the class instance creation expression. If U is not an inner class of O or O itself, then a compile-time error occurs.
Otherwise, let
O be the immediately enclosing class or interface declaration of C. Letn be an integer such that O is the n'th lexically enclosing class or interface declaration ofthe class or interface in which the class instance creation expression appearsU.The immediately enclosing instance of i is the n'th lexically enclosing instance of
this
.
If C is an inner member class, then:
If the class instance creation expression is unqualified, then:
If the class instance creation expression occurs in a static context, then a compile-time error occurs.
Otherwise, if C is not a member of any class whose declaration encloses the class instance creation expression, then a compile-time error occurs.
Otherwise, let O be the innermost enclosing class declaration of which C is a member, and let U be the immediately enclosing class or interface declaration of the class instance creation expression. If U is not an inner class of O or O itself, then a compile-time error occurs.
Otherwise,
if C is a member of a class enclosing the class or interface in which the class instance creation expression appears, then let O be the immediately enclosing class of which C is a member. Letlet n be an integer such that O is the n'th lexically enclosing class or interface declaration ofthe class or interface in which the class instance creation expression appearsU.The immediately enclosing instance of i is the n'th lexically enclosing instance of
this
.Otherwise, a compile-time error occurs.
If the class instance creation expression is qualified, then the immediately enclosing instance of i is the object that is the value of the Primary expression or the ExpressionName.
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:
If S is
aan inner local class, then:If S occurs in a static context, then i has no immediately enclosing instance with respect to S.
Otherwise, if the class instance creation expression occurs in a static context, then a compile-time error occurs.
Otherwise, let O be the immediately enclosing class or interface declaration of S, and let U be the immediately enclosing class or interface declaration of the class instance creation expression. If U is not an inner class of O or O itself, then a compile-time error occurs.
Otherwise, let
O be the immediately enclosing class or interface declaration of S. Letn be an integer such that O is the n'th lexically enclosing class or interface declaration ofthe class or interface in which the class instance creation expression appearsU.The immediately enclosing instance of i with respect to S is the n'th lexically enclosing instance of
this
.
If S is an inner member class, then:
If the class instance creation expression is unqualified, then:
If the class instance creation expression occurs in a static context, then a compile-time error occurs.
Otherwise, if S is not a member of any class whose declaration encloses the class instance creation expression, then a compile-time error occurs.
Otherwise, let O be the innermost enclosing class declaration of which S is a member, and let U be the immediately enclosing class or interface declaration of the class instance creation expression. If U is not an inner class of O or O itself, then a compile-time error occurs.
Otherwise,
if S is a member of a class enclosing the class or interface in which the class instance creation expression appears, then let O be the immediately enclosing class of which S is a member. Letlet n be an integer such that O is the n'th lexically enclosing class or interface declaration ofthe class or interface in which the class instance creation expression appearsU.The immediately enclosing instance of i with respect to S is the n'th lexically enclosing instance of
this
.Otherwise, a compile-time error occurs.
If the class instance creation expression is qualified, then the immediately enclosing instance of i with respect to S is the object that is the value of the Primary expression or the ExpressionName.
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 Class or Interface 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 class or interface 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 class or interface to search, there are six cases to consider, depending on the form that precedes the left parenthesis of the MethodInvocation:
If the form is MethodName, that is, just an Identifier, then:
If the Identifier appears in the scope of a method declaration with that name (6.3, 6.4.1), then:
If there is an enclosing class or interface declaration of which that method is a member, let T be the innermost such class or interface declaration. The class or interface to search is T.
This search policy is called the "comb rule". It effectively looks for methods in a nested class's superclass hierarchy before looking for methods in an enclosing class and its superclass hierarchy. See 6.5.7.1 for an example.
Otherwise, the method declaration may be in scope due to one or more single-static-import or static-import-on-demand declarations. There is no class or interface to search, as the method to be invoked is determined later (15.12.2.1).
If the form is TypeName
.
[TypeArguments] Identifier, then the type to search is the type denoted by TypeName.If the form is ExpressionName
.
[TypeArguments] Identifier, then the class or interface to search is the declared type T of the variable denoted by ExpressionName if T is a class or interface type, or the upper bound of T if T is a type variable.If the form is Primary
.
[TypeArguments] Identifier, then let T be the type of the Primary expression. The class or interface to search is T if T is a class or interface type, or the upper bound of T if T is a type variable.It is a compile-time error if T is not a reference type.
If the form is
super
.
[TypeArguments] Identifier, then the class to search is the superclass of the class whose declaration contains the method invocation.Let T be the class or interface declaration immediately enclosing the method invocation. It is a compile-time error if T is the class
Object
or T is an interface.If the form is TypeName
.
super
.
[TypeArguments] Identifier, then:It is a compile-time error if TypeName denotes neither a class nor an interface.
If TypeName denote a class, C, then the class to search is the superclass of C.
It is a compile-time error if C is not a lexically enclosing class or interface declaration of the
current class or interfacemethod invocation expression, or if C is the classObject
.C may be the current class.
Let T be the class or interface declaration immediately enclosing the method invocation. It is a compile-time error if T is the classObject
.This is unnecessary—
Object
has no enclosing type declaration, but if it did, there would be nothing wrong with referring to that class'ssuper
methods.Otherwise, TypeName denotes the interface to be searched, I.
Let T be the class or interface declaration immediately enclosing the method invocation. It is a compile-time error if I is not a direct superinterface of T, or if there exists some other direct superclass or direct superinterface of T, J, such that J is a subtype of I.
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 unqualifiedsuper
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 interfacesuper
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:
If the form is MethodName - that is, just an Identifier - and the compile-time declaration is an instance method, then:
It is a compile-time error if the method invocation occurs in a static context (8.1.3).
Otherwise, let C be the immediately enclosing class of which the compile-time declaration is a member. If the method invocation is not directly enclosed by C or an inner class of C, then a compile-time error occurs.Otherwise, let T be the class or interface to search (15.12.1). It is a compile-time error if the innermost enclosing class or interface declaration of the method invocation is neither T nor an inner class of T.
If the form is TypeName
.
[TypeArguments] Identifier, then the compile-time declaration must bestatic
, or a compile-time error occurs.If the form is ExpressionName
.
[TypeArguments] Identifier or Primary.
[TypeArguments] Identifier, then the compile-time declaration must not be astatic
method declared in an interface, or a compile-time error occurs.If the form is
super
.
[TypeArguments] Identifier, then:It is a compile-time error if the compile-time declaration is
abstract
.It is a compile-time error if the method invocation occurs in a static context.
If the form is TypeName
.
super
.
[TypeArguments] Identifier, then:It is a compile-time error if the compile-time declaration is
abstract
.It is a compile-time error if the method invocation occurs in a static context.
If TypeName denotes a class C, then if the immediately enclosing class or interface declaration of the method invocation is not
directly enclosed byC or an inner class of C, a compile-time error occurs.If TypeName denotes an interface, let
TU be the class or interface declaration immediately enclosing the method invocation. A compile-time error occurs if there exists a method, distinct from the compile-time declaration, that overrides (9.4.1) the compile-time declaration from a direct superclass or direct superinterface ofTU.In the case that a superinterface overrides a method declared in a grandparent interface, this rule prevents the child interface from "skipping" the override by simply adding the grandparent to its list of direct superinterfaces. The appropriate way to access functionality of a grandparent is through the direct superinterface, and only if that interface chooses to expose the desired behavior. (Alternately, the programmer is free to define an additional superinterface that exposes the desired behavior with a
super
method invocation.)
The compile-time parameter types and compile-time result are determined as follows:
If the compile-time declaration for the method invocation is not a signature polymorphic method, then:
The compile-time parameter types are the types of the formal parameters of the compile-time declaration.
The compile-time result is the result of the invocation type of the compile-time declaration (15.12.2.6).
If the compile-time declaration for the method invocation is a signature polymorphic method, then:
The compile-time parameter types are the types of the actual argument expressions. An argument expression which is the null literal
null
(3.10.7) is treated as having the typeVoid
.The compile-time result is determined as follows:
If the signature polymorphic method is either
void
or has a return type other thanObject
, the compile-time result is the result of the invocation type of the compile-time declaration (15.12.2.6).Otherwise, if the method invocation expression is an expression statement, the compile-time result is
void
.Otherwise, if the method invocation expression is the operand of a cast expression (15.16), the compile-time result is the erasure of the type of the cast expression (4.6).
Otherwise, the compile-time result is the signature polymorphic method's return type,
Object
.
A method is signature polymorphic if all of the following are true:
It is declared in the
java.lang.invoke.MethodHandle
class or thejava.lang.invoke.VarHandle
class.It has a single variable arity parameter (8.4.1) whose declared type is
Object[]
.It is
native
.
The following compile-time information is then associated with the method invocation for use at run time:
The name of the method.
The qualifying type of the method invocation (13.1).
The number of parameters and the compile-time parameter types, in order.
The compile-time result.
The invocation mode, computed as follows:
If the compile-time declaration has the
static
modifier, then the invocation mode isstatic
.Otherwise, if the part of the method invocation before the left parenthesis is of the form
super
.
Identifier or of the form TypeName.
super
.
Identifier, then the invocation mode issuper
.Otherwise, if the qualifying type of the method invocation is an interface, then the invocation mode is
interface
.Otherwise, the invocation mode is
virtual
.
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:
First, a type to search is determined:
If the method reference expression has the form ExpressionName
::
[TypeArguments] Identifier or Primary::
[TypeArguments] Identifier, the type to search is the type of the expression preceding the::
token.If the method reference expression has the form ReferenceType
::
[TypeArguments] Identifier, the type to search is the result of capture conversion (5.1.10) applied to ReferenceType.If the method reference expression has the form
super
::
[TypeArguments] Identifier, the type to search is the superclass type of theclass whose declaration containsimmediately enclosing class or interface declaration of the method reference.Let T be the class or interface declaration immediately enclosing the method reference expression. It is a compile-time error if T is the class
Object
or T is an interface.If the method reference expression has the form TypeName
.
super
::
[TypeArguments] Identifier, then if TypeName denotes a class, the type to search is the superclass type of the named class; otherwise, TypeName denotes an interface, and the corresponding superinterface type of the class or interface whose declaration contains the method reference is the typeto search.It is a compile-time error if TypeName is neither a lexically enclosing class or interface declaration of the method reference expression nor a direct superinterface of the immediately enclosing class or interface declaration of the method reference expression.
It is a compile-time error if TypeName is the class
Object
.It is a compile-time error if TypeName is an interface type, and there exists some other direct superclass or direct superinterface of the immediately enclosing class or interface declaration of the method reference expression, J, such that J is a subtype of TypeName.
Compare 15.12.1. These rules for method reference expressions don't seem to have been stated anywhere.
For the two other forms (involving
::
new
), the referenced method is notional and there is no type to search.
Second, given a targeted function type with n parameters, a set of potentially applicable methods is identified:
If the method reference expression has the form ReferenceType
::
[TypeArguments] Identifier, then the potentially applicable methods are:the member methods of the type to search that would be potentially applicable (15.12.2.1) for a method invocation which names Identifier, has arity n, has type arguments TypeArguments, and appears in the same class as the method reference expression; plus
the member methods of the type to search that would be potentially applicable for a method invocation which names Identifier, has arity n-1, has type arguments TypeArguments, and appears in the same class as the method reference expression.
Two different arities, n and n-1, are considered, to account for the possibility that this form refers to either a
static
method or an instance method.If the method reference expression has the form ClassType
::
[TypeArguments]new
, then the potentially applicable methods are a set of notional methods corresponding to the constructors of ClassType.If ClassType is a raw type, but is not a non-
static
member type of a raw type, the candidate notional member methods are those specified in 15.9.3 for a class instance creation expression that uses<>
to elide the type arguments to a class. Otherwise, the candidate notional member methods are the constructors of ClassType, treated as if they were methods with return type ClassType.Among these candidates, the potentially applicable methods are the notional methods that would be potentially applicable for a method invocation which has arity n, has type arguments TypeArguments, and appears in the same class as the method reference expression.
If the method reference expression has the form ArrayType
::
new
, a single notional method is considered. The method has a single parameter of typeint
, returns the ArrayType, and has nothrows
clause. If n = 1, this is the only potentially applicable method; otherwise, there are no potentially applicable methods.For all other forms, the potentially applicable methods are the member methods of the type to search that would be potentially applicable for a method invocation which names Identifier, has arity n, has type argument TypeArguments, and appears in the same class as the method reference expression.
Finally, if there are no potentially applicable methods, then there is no compile-time declaration.
Otherwise, given a targeted function type with parameter types P1, ..., Pn and a set of potentially applicable methods, the compile-time declaration is selected as follows:
If the method reference expression has the form ReferenceType
::
[TypeArguments] Identifier, then two searches for a most specific applicable method are performed. Each search is as specified in 15.12.2.2 through 15.12.2.5, with the clarifications below. Each search produces a set of applicable methods and, possibly, designates a most specific method of the set. In the case of an error as specified in 15.12.2.4, the set of applicable methods is empty. In the case of an error as specified in 15.12.2.5, there is no most specific method.In the first search, the method reference is treated as if it were an invocation with argument expressions of types P1, ..., Pn. Type arguments, if any, are given by the method reference expression.
In the second search, if P1, ..., Pn is not empty and P1 is a subtype of ReferenceType, then the method reference expression is treated as if it were a method invocation expression with argument expressions of types P2, ..., Pn. If ReferenceType is a raw type, and there exists a parameterization of this type, G
<
...>
, that is a supertype of P1, the type to search is the result of capture conversion (5.1.10) applied to G<
...>
; otherwise, the type to search is the same as the type of the first search. Type arguments, if any, are given by the method reference expression.If the first search produces a most specific method that is
static
, and the set of applicable methods produced by the second search contains no non-static
methods, then the compile-time declaration is the most specified method of the first search.Otherwise, if the set of applicable methods produced by the first search contains no
static
methods, and the second search produces a most specific method that is non-static
, then the compile-time declaration is the most specific method of the second search.Otherwise, there is no compile-time declaration.
For all other forms of method reference expression, one search for a most specific applicable method is performed. The search is as specified in 15.12.2.2 through 15.12.2.5, with the clarifications below.
The method reference is treated as if it were an invocation with argument expressions of types P1, ..., Pn; the type arguments, if any, are given by the method reference expression.
If the search results in an error as specified in 15.12.2.2 through 15.12.2.5, or if the most specific applicable method is
static
, there is no compile-time declaration.Otherwise, the compile-time declaration is the most specific applicable method.
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 astatic
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 ofPair<String,Integer>::first
. Similarly, a method reference likePair::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:
If the method reference expression has the form ReferenceType
::
[TypeArguments] Identifier, then ReferenceType does not denote a raw type.The type to search has exactly one member method with the name Identifier that is accessible to the class or interface in which the method reference expression appears.
This method is not variable arity (8.4.1).
If this method is generic (8.4.4), then the method reference expression provides TypeArguments.
A method reference expression of the form ClassType ::
[TypeArguments] new
is exact if it satisfies all of the following:
The type denoted by ClassType is not raw, or is a non-
static
member type of a raw type.The type denoted by ClassType has exactly one constructor that is accessible to the class or interface in which the method reference expression appears.
This constructor is not variable arity.
If this constructor is generic, then the method reference expression provides TypeArguments.
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:
If a formal parameter is specified either by a parameter specifier that uses
var
, or by an identifier instead of a parameter specifier, then the formal parameter has an inferred type. The type is inferred from the functional interface type targeted by the lambda expression (15.27.3).If a formal parameter is specified by a parameter specifier that does not use
var
, then the formal parameter has a declared type. The declared type is determined as follows:If the formal parameter is not a variable arity parameter, then the declared type is denoted by UnannType if no bracket pairs appear in UnannType and VariableDeclaratorId, and specified by 10.2 otherwise.
If the formal parameter is a variable arity parameter, then the declared type is an array type specified by 10.2.
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 usesint[]
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 andvar
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 viathis
), 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
- V is [un]assigned after a local class or interface declaration
statement(14.3) iff V is [un]assigned before the local class or interface declarationstatement.