This document describes changes to the Java Language Specification to enhance the switch
statement, including:
A new body form that evaluates expressions or blocks without fallthrough.
A new
switch
expression which, along with a newyield
statement, allows a switch to produce a value.
See JEP 361 for an overview.
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 3: Lexical Structure
3.8 Identifiers
An identifier is an unlimited-length sequence of Java letters and Java digits, the first of which must be a Java letter.
- Identifier:
- IdentifierChars but not a Keyword or BooleanLiteral or NullLiteral
- IdentifierChars:
- JavaLetter {JavaLetterOrDigit}
- JavaLetter:
- any Unicode character that is a "Java letter"
- JavaLetterOrDigit:
- any Unicode character that is a "Java letter-or-digit"
A "Java letter" is a character for which the method Character.isJavaIdentifierStart(int)
returns true.
A "Java letter-or-digit" is a character for which the method Character.isJavaIdentifierPart(int)
returns true.
The "Java letters" include uppercase and lowercase ASCII Latin letters
A-Z
(\u0041-\u005a
), anda-z
(\u0061-\u007a
), and, for historical reasons, the ASCII dollar sign ($
, or\u0024
) and underscore (_
, or\u005f
). The dollar sign should be used only in mechanically generated source code or, rarely, to access pre-existing names on legacy systems. The underscore may be used in identifiers formed of two or more characters, but it cannot be used as a one-character identifier due to being a keyword.
The "Java digits" include the ASCII digits
0-9
(\u0030-\u0039
).
Letters and digits may be drawn from the entire Unicode character set, which supports most writing scripts in use in the world today, including the large sets for Chinese, Japanese, and Korean. This allows programmers to use identifiers in their programs that are written in their native languages.
An identifier cannot have the same spelling (Unicode character sequence) as a keyword (3.9), boolean literal (3.10.3), or the null literal (3.10.7), or a compile-time error occurs.
Two identifiers are the same only if, after ignoring characters that are ignorable, the identifiers have the same Unicode character for each letter or digit. An ignorable character is a character for which the method Character.isIdentifierIgnorable(int)
returns true. Identifiers that have the same external appearance may yet be different.
For example, the identifiers consisting of the single letters LATIN CAPITAL LETTER A (
A
,\u0041
), LATIN SMALL LETTER A (a
,\u0061
), GREEK CAPITAL LETTER ALPHA (A
,\u0391
), CYRILLIC SMALL LETTER A (a
,\u0430
) and MATHEMATICAL BOLD ITALIC SMALL A (a
,\ud835\udc82
) are all different.Unicode composite characters are different from their canonical equivalent decomposed characters. For example, a LATIN CAPITAL LETTER A ACUTE (
Á
,\u00c1
) is different from a LATIN CAPITAL LETTER A (A
,\u0041
) immediately followed by a NON-SPACING ACUTE (´
,\u0301
) in identifiers. See The Unicode Standard, Section 3.11 "Normalization Forms".
Examples of identifiers are:
String
i3
- αρετη
MAX_VALUE
isLetterOrDigit
The identifiers var
and yield
are restricted identifiers because they are not allowed in some contexts.
A type identifier is an identifier that is not the character sequence var
, or the character sequence yield
.
- TypeIdentifier:
- Identifier but not
var
oryield
Type identifiers are used in certain contexts involving the declaration or use of types. For example, the name of a class must be a TypeIdentifier, so it is illegal to declare a class named
var
oryield
(8.1).
An unqualified method identifier is an identifier that is not the character sequence yield
.
- UnqualifiedMethodIdentifier:
- Identifier but not
yield
This restriction allows
yield
to be used in ayield
statement (14.21) and still also be used as a (qualified) method name for compatibility reasons.
3.9 Keywords
51 character sequences, formed from ASCII letters, are reserved for use as keywords and cannot be used as identifiers (3.8).
- Keyword:
- (one of)
abstract continue for new switch
assert default if package synchronized
boolean do goto private this
break double implements protected throw
byte else import public throws
case enum instanceof return transient
catch extends int short try
char final interface static void
class finally long strictfp volatile
const float native super while
_
(underscore)
The keywords
const
andgoto
are reserved, even though they are not currently used. This may allow a Java compiler to produce better error messages if these C++ keywords incorrectly appear in programs.
A variety of character sequences are sometimes assumed, incorrectly, to be keywords:
true
andfalse
are not keywords, but rather boolean literals (3.10.3).
null
is not a keyword, but rather the null literal (3.10.7).
var
is not a keyword, but rather an identifier with special meaning as the type of a local variable declaration (14.4, 14.14.1, 14.14.2, 14.20.3) and the type of a lambda formal parameter (15.27.1).
- The restricted identifiers
var
andyield
are not keywords.var
has special meaning as the type of a local variable declaration (14.4, 14.14.1, 14.14.2, 14.20.3) and the type of a lambda formal parameter (15.27.1).yield
has special meaning in ayield
statement (14.21). All invocations of a method calledyield
must be qualified so as to be distinguished from ayield
statement.
A further ten character sequences are restricted keywords: open
, module
, requires
, transitive
, exports
, opens
, to
, uses
, provides
, and with
. These character sequences are tokenized as keywords solely where they appear as terminals in the ModuleDeclaration, ModuleDirective, and RequiresModifier productions (7.7). They are tokenized as identifiers everywhere else, for compatibility with programs written before the introduction of restricted keywords. There is one exception: immediately to the right of the character sequence requires
in the ModuleDirective production, the character sequence transitive
is tokenized as a keyword unless it is followed by a separator, in which case it is tokenized as an identifier.
Chapter 5: Conversions and Contexts
Every expression written in the Java programming language either produces no result (15.1) or has a type that can be deduced at compile time (15.3). When an expression appears in most contexts, it must be compatible with a type expected in that context; this type is called the target type. For convenience, compatibility of an expression with its surrounding context is facilitated in two ways:
First, for some expressions, termed poly expressions (15.2), the deduced type can be influenced by the target type. The same expression can have different types in different contexts.
Second, after the type of the expression has been deduced, an implicit conversion from the type of the expression to the target type can sometimes be performed.
If neither strategy is able to produce the appropriate type, a compile-time error occurs.
The rules determining whether an expression is a poly expression, and if so, its type and compatibility in a particular context, vary depending on the kind of context and the form of the expression. In addition to influencing the type of the expression, the target type may in some cases influence the run time behavior of the expression in order to produce a value of the appropriate type.
Similarly, the rules determining whether a target type allows an implicit conversion vary depending on the kind of context, the type of the expression, and, in one special case, the value of a constant expression (15.28 15.29). A conversion from type S to type T allows an expression of type S to be treated at compile time as if it had type T instead. In some cases this will require a corresponding action at run time to check the validity of the conversion or to translate the run-time value of the expression into a form appropriate for the new type T.
Example 5.0-1. Conversions at Compile Time and Run Time
A conversion from type
Object
to typeThread
requires a run-time check to make sure that the run-time value is actually an instance of classThread
or one of its subclasses; if it is not, an exception is thrown.A conversion from type
Thread
to typeObject
requires no run-time action;Thread
is a subclass ofObject
, so any reference produced by an expression of typeThread
is a valid reference value of typeObject
.A conversion from type
int
to typelong
requires run-time sign-extension of a 32-bit integer value to the 64-bitlong
representation. No information is lost.A conversion from type
double
to typelong
requires a non-trivial translation from a 64-bit floating-point value to the 64-bit integer representation. Depending on the actual run-time value, information may be lost.
The conversions possible in the Java programming language are grouped into several broad categories:
- Identity conversions
- Widening primitive conversions
- Narrowing primitive conversions
- Widening reference conversions
- Narrowing reference conversions
- Boxing conversions
- Unboxing conversions
- Unchecked conversions
- Capture conversions
- String conversions
- Value set conversions
There are six kinds of conversion contexts in which poly expressions may be influenced by context or implicit conversions may occur. Each kind of context has different rules for poly expression typing and allows conversions in some of the categories above but not others. The contexts are:
Assignment contexts (5.2, 15.26), in which an expression's value is bound to a named variable. Primitive and reference types are subject to widening, values may be boxed or unboxed, and some primitive constant expressions may be subject to narrowing. An unchecked conversion may also occur.
Strict invocation contexts (5.3, 15.9, 15.12), in which an argument is bound to a formal parameter of a constructor or method. Widening primitive, widening reference, and unchecked conversions may occur.
Loose invocation contexts (5.3, 15.9, 15.12), in which, like strict invocation contexts, an argument is bound to a formal parameter. Method or constructor invocations may provide this context if no applicable declaration can be found using only strict invocation contexts. In addition to widening and unchecked conversions, this context allows boxing and unboxing conversions to occur.
String contexts (5.4, 15.18.1), in which a value of any type is converted to an object of type
String
.Casting contexts (5.5), in which an expression's value is converted to a type explicitly specified by a cast operator (15.16). Casting contexts are more inclusive than assignment or loose invocation contexts, allowing any specific conversion other than a string conversion, but certain casts to a reference type are checked for correctness at run time.
Numeric contexts (5.6), in which the operands of a numeric operator or some other expressions that operate on numbers may be widened to a common type
so that an operation can be performed.
The term "conversion" is also used to describe, without being specific, any conversions allowed in a particular context. For example, we say that an expression that is the initializer of a local variable is subject to "assignment conversion", meaning that a specific conversion will be implicitly chosen for that expression according to the rules for the assignment context.
Example 5.0-2. Conversions In Various Contexts
class Test {
public static void main(String[] args) {
// Casting conversion (5.4) of a float literal to
// type int. Without the cast operator, this would
// be a compile-time error, because this is a
// narrowing conversion (5.1.3):
int i = (int)12.5f;
// String conversion (5.4) of i's int value:
System.out.println("(int)12.5f==" + i);
// Assignment conversion (5.2) of i's value to type
// float. This is a widening conversion (5.1.2):
float f = i;
// String conversion of f's float value:
System.out.println("after float widening: " + f);
// Numeric promotion (5.6) of i's value to type
// float. This is a binary numeric promotion.
// After promotion, the operation is float*float:
System.out.print(f);
f = f * i;
// Two string conversions of i and f:
System.out.println("*" + i + "==" + f);
// Invocation conversion (5.3) of f's value
// to type double, needed because the method Math.sin
// accepts only a double argument:
double d = Math.sin(f);
// Two string conversions of f and d:
System.out.println("Math.sin(" + f + ")==" + d);
}
}
This program produces the output:
(int)12.5f==12
after float widening: 12.0
12.0*12==144.0
Math.sin(144.0)==-0.49102159389846934
5.6 Numeric Contexts
Numeric contexts apply to the operands of an arithmetic operator.
Numeric contexts allow the use of:
an identity conversion (5.1.1)
a widening primitive conversion (5.1.2)
a widening reference conversion (5.1.5) followed by an unboxing conversion
a widening reference conversion followed by an unboxing conversion, then followed by a widening primitive conversion
an unboxing conversion (5.1.8)
an unboxing conversion followed by a widening primitive conversion
A numeric promotion is a process by which, given an arithmetic operator and its argument expressions, the arguments are converted to an inferred target type T. T is chosen during promotion such that each argument expression can be converted to T and the arithmetic operation is defined for values of type T.
The two kinds of numeric promotion are unary numeric promotion (5.6.1) and binary numeric promotion (5.6.2).
The entire section 5.6 has been re-organised and is replaced with the following text. (Sections 5.6.1 and 5.6.2 are now inlined in the section.)
Numeric contexts apply to the operands of arithmetic operators, array creation and access expressions, conditional expressions, and the result expressions of switch
expressions.
An expression appears in a numeric context if it is one of:
the dimension expression in an array creation expression (15.10.1)
the index expression in an array access expression (15.10.3)
the operand of a unary operator
+
,-
, or~
an operand of a binary operator
-
,*
,/
,%
,<
,<=
,>
, or>=
an operand of a shift operator
<<
,>>
, or>>>
. Operands to these shift operators are treated separately rather than as a group. Along
shift distance (right operand) does not promote the value being shifted (left operand) tolong
.an operand of a binary operator
+
,==
,!=
,&
,^
, or|
, where the operands are both convertible to a numeric typethe second or third operand of a numeric conditional expression (15.25.2)
a result expression of a standalone
switch
expression (15.28.1), where all the result expressions are convertible to a numeric type
The number of expressions in a numeric context depends on the context. Some contexts have exactly one expression, some contexts have exactly two, and some have an arbitrary number.
Numeric contexts are divided into three kinds:
A numeric array context is a numeric context that is either an array creation expression, or an array access expression.
A numeric choice context is a numeric context that is either a numeric conditional expression or a standalone
switch
expression where all the result expressions are convertible to a numeric type.A numeric arithmetic context is a numeric context that is not a numeric array context and is not a numeric choice context.
Numeric promotion is a process that determines a promoted type for the expressions in a numeric context. This promoted type, T, is chosen such that each expression can be converted to T and, in the case of an arithmetic operation, the operation is defined for values of type T. The order of expressions in a numeric context is not significant in the process of numeric promotion.
Numeric promotion applies the following steps:
If any expression is of a reference type, it is subjected to unboxing conversion (5.1.8).
Next, widening primitive conversion (5.1.2) and narrowing primitive conversion (5.1.3) are applied to some expressions in the process of determining a promoted type, as specified by the following rules:
If any expression is of type
double
, then the promoted type isdouble
, and other expressions that are not of typedouble
undergo widening primitive conversion todouble
.Otherwise, if any expression is of type
float
, then the promoted type isfloat
and other expressions that are not of typefloat
undergo widening primitive conversion tofloat
.Otherwise, if any expression is of type
long
, then the promoted type islong
and other expressions that are not of typelong
undergo widening primitive conversion tolong
.Otherwise, if any expression appears in a numeric array context or a numeric arithmetic context, rather than a numeric choice context, then the promoted type is
int
and other expressions that are not of typeint
undergo widening primitive conversion toint
.Otherwise, the expression is in a numeric choice context and the following rules apply:
If any expression is of type
int
and is not a constant expression, then T isint
and other expressions that are not of typeint
undergo widening primitive conversion toint
.Otherwise, if any expression is of type
short
, and every other expression is either of typeshort
, or of typebyte
, or a constant expression of typeint
with a value that is representable in the typeshort
, then T isshort
, thebyte
expressions undergo widening primitive conversion toshort
, and theint
expressions undergo narrowing primitive conversion toshort
.Otherwise, if any expression is of type
byte
, and every other expression is either of typebyte
or a constant expression of typeint
with a value that is representable in the typebyte
, then T isbyte
and theint
expressions undergo narrowing primitive conversion tobyte
.Otherwise, if any expression is of type
char
, and every other expression is either of typechar
or a constant expression of typeint
with a value that is representable in the typechar
, then T ischar
and theint
expressions undergo narrowing primitive conversion tochar
.Otherwise, T is
int
and all the expressions that are not of typeint
undergo widening primitive conversion toint
.
After the conversion(s), if any, value set conversion (5.1.13) is then applied to each expression.
A unary numeric promotion applies numeric promotion to a single expression that occurs in a numeric context. A binary numeric promotion is performed on a pair of expressions that occur in a numeric context that is not a numeric choice context.
Example 5.6-1. Unary Numeric Promotion
class Test {
public static void main(String[] args) {
byte b = 2;
int a[] = new int[b]; // dimension expression promotion
char c = '\u0001';
a[c] = 1; // index expression promotion
a[0] = -c; // unary - promotion
System.out.println("a: " + a[0] + "," + a[1]);
b = -1;
int i = ~b; // bitwise complement promotion
System.out.println("~0x" + Integer.toHexString(b)
+ "==0x" + Integer.toHexString(i));
i = b << 4L; // shift promotion (left operand)
System.out.println("0x" + Integer.toHexString(b)
+ "<<4L==0x" + Integer.toHexString(i));
}
}
This program produces the output:
a: -1,1
~0xffffffff==0x0
0xffffffff<<4L==0xfffffff0
Example 5.6-2. Binary Numeric Promotion
class Test {
public static void main(String[] args) {
int i = 0;
float f = 1.0f;
double d = 2.0;
// First int*float is promoted to float*float, then
// float==double is promoted to double==double:
if (i * f == d) System.out.println("oops");
// A char&byte is promoted to int&int:
byte b = 0x1f;
char c = 'G';
int control = c & b;
System.out.println(Integer.toHexString(control));
// Here int:float is promoted to float:float:
f = (b==0) ? i : 4.0f;
System.out.println(1.0/f);
}
}
This program produces the output:
7
0.25
The example converts the ASCII character G
to the ASCII control-G (BEL), by masking off all but the low 5 bits of the character. The 7
is the numeric value of this control character.
5.6.1 Unary Numeric Promotion
Some operators apply unary numeric promotion to a single operand, which must produce a value of a numeric type:
If the operand is of compile-time type
Byte
,Short
,Character
, orInteger
, it is subjected to unboxing conversion (5.1.8). The result is then promoted to a value of typeint
by a widening primitive conversion (5.1.2) or an identity conversion (5.1.1).Otherwise, if the operand is of compile-time type
Long
,Float
, orDouble
, it is subjected to unboxing conversion (5.1.8).Otherwise, if the operand is of compile-time type
byte
,short
, orchar
, it is promoted to a value of typeint
by a widening primitive conversion (5.1.2).Otherwise, a unary numeric operand remains as is and is not converted.
After the conversion(s), if any, value set conversion (5.1.13) is then applied.
Unary numeric promotion is performed on expressions in the following situations:
Each dimension expression in an array creation expression (15.10.1)
The index expression in an array access expression (15.10.3)
The operand of a unary plus operator
+
(15.15.3)The operand of a unary minus operator
-
(15.15.4)The operand of a bitwise complement operator
~
(15.15.5)Each operand, separately, of a shift operator
<<
,>>
, or>>>
(15.19).A
long
shift distance (right operand) does not promote the value being shifted (left operand) tolong
.
Example 5.6.1-1. Unary Numeric Promotion
class Test {
public static void main(String[] args) {
byte b = 2;
int a[] = new int[b]; // dimension expression promotion
char c = '\u0001';
a[c] = 1; // index expression promotion
a[0] = -c; // unary - promotion
System.out.println("a: " + a[0] + "," + a[1]);
b = -1;
int i = ~b; // bitwise complement promotion
System.out.println("~0x" + Integer.toHexString(b)
+ "==0x" + Integer.toHexString(i));
i = b << 4L; // shift promotion (left operand)
System.out.println("0x" + Integer.toHexString(b)
+ "<<4L==0x" + Integer.toHexString(i));
}
}
This program produces the output:
a: -1,1
~0xffffffff==0x0
0xffffffff<<4L==0xfffffff0
5.6.2 Binary Numeric Promotion
When an operator applies binary numeric promotion to a pair of operands, each of which must denote a value that is convertible to a numeric type, the following rules apply, in order:
If any operand is of a reference type, it is subjected to unboxing conversion (5.1.8).
Widening primitive conversion (5.1.2) is applied to convert either or both operands as specified by the following rules:
If either operand is of type
double
, the other is converted todouble
.Otherwise, if either operand is of type
float
, the other is converted tofloat
.Otherwise, if either operand is of type
long
, the other is converted tolong
.Otherwise, both operands are converted to type
int
.
After the conversion(s), if any, value set conversion (5.1.13) is then applied to each operand.
Binary numeric promotion is performed on the operands of certain operators:
The multiplicative operators
*
,/
, and%
(15.17)The addition and subtraction operators for numeric types
+
and-
(15.18.2)The numerical comparison operators
<
,<=
,>
, and>=
(15.20.1)The numerical equality operators
==
and!=
(15.21.1)The integer bitwise operators
&
,^
, and|
(15.22.1)In certain cases, the conditional operator
? :
(15.25)
Example 5.6.2-1. Binary Numeric Promotion
class Test {
public static void main(String[] args) {
int i = 0;
float f = 1.0f;
double d = 2.0;
// First int*float is promoted to float*float, then
// float==double is promoted to double==double:
if (i * f == d) System.out.println("oops");
// A char&byte is promoted to int&int:
byte b = 0x1f;
char c = 'G';
int control = c & b;
System.out.println(Integer.toHexString(control));
// Here int:float is promoted to float:float:
f = (b==0) ? i : 4.0f;
System.out.println(1.0/f);
}
}
This program produces the output:
7
0.25
The example converts the ASCII character G
to the ASCII control-G (BEL), by masking off all but the low 5 bits of the character. The 7
is the numeric value of this control character.
Chapter 6: Names
6.5 Determining the Meaning of a Name
The meaning of a name depends on the context in which it is used. The determination of the meaning of a name requires three steps:
First, context causes a name syntactically to fall into one of seven categories: ModuleName, PackageName, TypeName, ExpressionName, MethodName, PackageOrTypeName, or AmbiguousName.
TypeName and MethodName
isare less expressive than the othersixfive categories, becauseit isthey are denoted with TypeIdentifier and UnqualifiedMethodIdentifier, respectively. In the former case this, whichexcludes the charactersequencesequencesvar
andyield
(3.8), and in the later it excludes the character sequenceyield
.Second, a name that is initially classified by its context as an AmbiguousName or as a PackageOrTypeName is then reclassified to be a PackageName, TypeName, or ExpressionName.
Third, the resulting category then dictates the final determination of the meaning of the name (or a compile-time error if the name has no meaning).
- ModuleName:
- Identifier
- ModuleName
.
Identifier - PackageName:
- Identifier
- PackageName
.
Identifier - TypeName:
- TypeIdentifier
- PackageOrTypeName
.
TypeIdentifier - PackageOrTypeName:
- Identifier
- PackageOrTypeName
.
Identifier - ExpressionName:
- Identifier
- AmbiguousName
.
Identifier - MethodName:
IdentifierUnqualifiedMethodIdentifier- AmbiguousName:
- Identifier
- AmbiguousName
.
Identifier
The use of context helps to minimize name conflicts between entities of different kinds. Such conflicts will be rare if the naming conventions described in 6.1 are followed. Nevertheless, conflicts may arise unintentionally as types developed by different programmers or different organizations evolve. For example, types, methods, and fields may have the same name. It is always possible to distinguish between a method and a field with the same name, since the context of a use always tells whether a method is intended.
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) 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.
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) with that name, then the AmbiguousName is reclassified as a TypeName.
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.
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.
Chapter 11: Exceptions
11.2 Compile-Time Checking of Exceptions
11.2.1 Exception Analysis of Expressions
A class instance creation expression (15.9) can throw an exception class E iff either:
The expression is a qualified class instance creation expression and the qualifying expression can throw E; or
Some expression of the argument list can throw E; or
E is one of the exception types of the invocation type of the chosen constructor (15.12.2.6); or
The class instance creation expression includes a ClassBody, and some instance initializer or instance variable initializer in the ClassBody can throw E.
A method invocation expression (15.12) can throw an exception class E iff either:
The method invocation expression is of the form Primary
.
[TypeArguments] Identifier and the Primary expression can throw E; orSome expression of the argument list can throw E; or
E is one of the exception types of the invocation type of the chosen method (15.12.2.6).
A switch
expression (15.28) can throw an exception class E iff either:
The selector expression can throw E; or
Some switch rule expression, switch rule block, switch rule
throw
statement, or switch labeled statement group in the switch block can throw E.
A lambda expression (15.27) can throw no exception classes.
For every other kind of expression, the expression can throw an exception class E iff one of its immediate subexpressions can throw E.
Note that a method reference expression (15.13) of the form Primary
::
[TypeArguments] Identifier can throw an exception class if the Primary subexpression can throw an exception class. In contrast, a lambda expression can throw nothing, and has no immediate subexpressions on which to perform exception analysis. It is the body of a lambda expression, containing expressions and statements, that can throw exception classes.
11.2.2 Exception Analysis of Statements
A throw
statement (14.18) whose thrown expression has static type E and is not a final or effectively final exception parameter can throw E or any exception class that the thrown expression can throw.
For example, the statement
throw new java.io.FileNotFoundException();
can throwjava.io.FileNotFoundException
only. Formally, it is not the case that it "can throw" a subclass or superclass ofjava.io.FileNotFoundException
.
A throw
statement whose thrown expression is a final or effectively final exception parameter of a catch
clause C can throw an exception class E iff:
E is an exception class that the
try
block of thetry
statement which declares C can throw; andE is assignment compatible with any of C's catchable exception classes; and
E is not assignment compatible with any of the catchable exception classes of the
catch
clauses declared to the left of C in the sametry
statement.
A try
statement (14.20) can throw an exception class E iff either:
The
try
block can throw E, or an expression used to initialize a resource (in atry
-with-resources statement) can throw E, or the automatic invocation of theclose()
method of a resource (in atry
-with-resources statement) can throw E, and E is not assignment compatible with any catchable exception class of anycatch
clause of thetry
statement, and either nofinally
block is present or thefinally
block can complete normally; orSome
catch
block of thetry
statement can throw E and either nofinally
block is present or thefinally
block can complete normally; orA
finally
block is present and can throw E.
An explicit constructor invocation statement (8.8.7.1) can throw an exception class E iff either:
Some expression of the constructor invocation's parameter list can throw E; or
E is determined to be an exception class of the
throws
clause of the constructor that is invoked (15.12.2.6).
A switch
statement (14.11) can throw an exception class E iff either:
The selector expression can throw E; or
Some switch rule expression, switch rule block, switch rule
throw
statement, or switch labeled statement group in the switch block can throw E.
Any other statement S can throw an exception class E iff an expression or statement immediately contained in S can throw E.
Chapter 14: Blocks and Statements
14.1 Normal and Abrupt Completion of Statements
Every statement has a normal mode of execution in which certain computational steps are carried out. The following sections describe the normal mode of execution for each kind of statement.
If all the steps are carried out as described, with no indication of abrupt completion, the statement is said to complete normally. However, certain events may prevent a statement from completing normally:
The
break
(14.15),continue
(14.16),andreturn
(14.17), andyield
(14.21), statements cause a transfer of control that may prevent normal completion of expressions, statements, and blocks that may contain them.Evaluation of certain expressions may throw exceptions from the Java Virtual Machine (15.6). An explicit
throw
(14.18) statement also results in an exception. An exception causes a transfer of control that may prevent normal completion of statements.
If such an event occurs, then execution of one or more statements may be terminated before all steps of their normal mode of execution have completed; such statements are said to complete abruptly.
An abrupt completion always has an associated reason, which is one of the following:
A
break
with no labelA
break
with a given labelA
continue
with no labelA
continue
with a given labelA
return
with no valueA
return
with a given valueA
throw
with a given value, including exceptions thrown by the Java Virtual Machine
- A
yield
with a given value
The terms "complete normally" and "complete abruptly" also apply to the evaluation of expressions (15.6). The only reason an expression can complete abruptly is that an exception is thrown, because of either a throw
with a given value (14.18) or a run-time exception or error (11, 15.6).
If a statement evaluates an expression, abrupt completion of the expression always causes the immediate abrupt completion of the statement, with the same reason. All succeeding steps in the normal mode of execution are not performed.
Unless otherwise specified in this chapter, abrupt completion of a substatement causes the immediate abrupt completion of the statement itself, with the same reason, and all succeeding steps in the normal mode of execution of the statement are not performed.
Unless otherwise specified, a statement completes normally if all expressions it evaluates and all substatements it executes complete normally.
14.5 Statements
There are many kinds of statements in the Java programming language. Most correspond to statements in the C and C++ languages, but some are unique.
As in C and C++, the if
statement of the Java programming language suffers from the so-called "dangling else
problem," illustrated by this misleadingly formatted example:
if (door.isOpen())
if (resident.isVisible())
resident.greet("Hello!");
else door.bell.ring(); // A "dangling else"
The problem is that both the outer if
statement and the inner if
statement might conceivably own the else
clause. In this example, one might surmise that the programmer intended the else
clause to belong to the outer if
statement.
The Java programming language, like C and C++ and many programming languages before them, arbitrarily decrees that an else
clause belongs to the innermost if
to which it might possibly belong. This rule is captured by the following grammar:
- Statement:
- StatementWithoutTrailingSubstatement
- LabeledStatement
- IfThenStatement
- IfThenElseStatement
- WhileStatement
- ForStatement
- StatementNoShortIf:
- StatementWithoutTrailingSubstatement
- LabeledStatementNoShortIf
- IfThenElseStatementNoShortIf
- WhileStatementNoShortIf
- ForStatementNoShortIf
- StatementWithoutTrailingSubstatement:
- Block
- EmptyStatement
- ExpressionStatement
- AssertStatement
- SwitchStatement
- DoStatement
- BreakStatement
- ContinueStatement
- ReturnStatement
- SynchronizedStatement
- ThrowStatement
- TryStatement
- YieldStatement
The following productions from [14.9] are shown here for convenience:
- IfThenStatement:
if
(
Expression)
Statement- IfThenElseStatement:
if
(
Expression)
StatementNoShortIfelse
Statement- IfThenElseStatementNoShortIf:
if
(
Expression)
StatementNoShortIfelse
StatementNoShortIf
Statements are thus grammatically divided into two categories: those that might end in an if
statement that has no else
clause (a "short if
statement") and those that definitely do not.
Only statements that definitely do not end in a short if
statement may appear as an immediate substatement before the keyword else
in an if
statement that does have an else
clause.
This simple rule prevents the "dangling else
" problem. The execution behavior of a statement with the "no short if
" restriction is identical to the execution behavior of the same kind of statement without the "no short if
" restriction; the distinction is drawn purely to resolve the syntactic difficulty.
14.11 The switch
Statement
Much of this section has been moved into subsections.
The switch
statement transfers control to one of several statements depending on the value of an expression. This expression is known as the selector expression.
- SwitchStatement:
switch
(
Expression)
SwitchBlock
- SwitchBlock:
{
{SwitchBlockStatementGroup} {SwitchLabel}}
- SwitchBlockStatementGroup:
- SwitchLabels BlockStatements
- SwitchLabels:
- SwitchLabel {SwitchLabel}
- SwitchLabel:
case
ConstantExpression:
case
EnumConstantName:
default
:
- EnumConstantName:
- Identifier
The type of the Expression must be char
, byte
, short
, int
, Character
, Byte
, Short
, Integer
, String
, or an enum type (8.9), or a compile-time error occurs.
14.11.1 The Switch Block
- SwitchBlock:
{
SwitchRule {SwitchRule}}
{
{SwitchLabeledStatementGroup} {SwitchLabel:
}}
- SwitchLabeledRule:
- SwitchLabel
->
Expression;
- SwitchLabel
->
Block - SwitchLabel
->
ThrowStatement - SwitchLabel:
case
CaseConstant {,
CaseConstant}default
- CaseConstant:
- ConditionalExpression
- SwitchLabeledStatementGroup:
- SwitchLabel
:
{SwitchLabel:
} BlockStatements
The body of both a switch
statement and a switch
expression (15.28) is known as a switch block. This block can consist of either:
Switch rules, which use
->
to introduce either a switch rule expression, a switch rule block, or a switch rulethrow
statement; orSwitch labeled statement groups, which use
:
to introduce switch labeled block statements.
Any statement immediately contained by the switch block may be labeled with one or more switch labels, which are A switch rule has a switch label and either an expression, a block, or a case
or default
labels.throw
statement. A switch labeled statement group has one or more switch labels and zero or more block statements. A switch label is either a case
or default
label. Every case
label has a one or more case
constants, each of which is either a constant expression or the name of an enum constant. Switch labels and their case
constants are said to be associated with the switch
statement block.
Every case
constant associated with a given switch block must be either a constant expression (15.29) or the name of an enum constant ([8.9.1]), or a compile-time error occurs.
This excludes the possibility of using
null
as acase
constant.
Given a switch
statement, all of the following must be true or a compile-time error occurs:
Every
case
constant associated with theswitch
statement must be assignment compatible with the type of theswitch
statement's Expression (5.2).If the type of the
switch
statement's Expression is an enum type, then everycase
constant associated with theswitch
statement must be an enum constant of that type.No two of the
case
constants associated with theswitch
statement have the same value.No
case
constant associated with theswitch
statement isnull
.At most one
default
label is associated with theswitch
statement.
A switch block is said to be compatible with the type of the selector expression, T, if both of the following are true:
If T is not an enum type, then every
case
constant associated with the switch block is assignment compatible with T (5.2).If T is an enum type, then every
case
constant associated with the switch block is an enum constant of type T.
At run time, the value of the selector expression is compared with each case
constant (if the selector expression is a reference type that is convertible to a numeric type, then it is first subject to an unboxing conversion (5.1.8)):
If one of the
case
constants is equal to the value of the selector expression, then we say that thecase
label matches. Equality is defined in terms of the==
operator (15.21) unless the value of the selector expression is aString
, in which case equality is defined in terms of theequals
method of classString
.If no
case
label matches but there is adefault
label, then we say that thedefault
label matches.
A
case
label can contain severalcase
constants, and matches the value of a selector expression if any one of itscase
constants matches.switch (day) { case SATURDAY, SUNDAY: // matches if it is a Saturday OR a Sunday System.out.println("It's the weekend!"); }
The prohibition against using
null
as acase
constant prevents code being written that can never be executed. If theselector expression is of a reference type, that is,switch
statement's ExpressionString
or a boxed primitive type or an enum type, then an exceptionwill be thrownwill occur if the Expression evaluates tonull
at run time. In the judgment of the designers of the Java programming language, this is a better outcome thansilently skipping the entireeither the switch block not matching or even theswitch
statement or choosing to execute the statements (if any) after thedefault
label (if any)default
label matching.
A Java compiler is encouraged (but not required) to provide a warning if aswitch
on an enum-valued expression lacks adefault
label and lackscase
labels for one or more of the enum's constants. Such aswitch
will silently do nothing if the expression evaluates to one of the missing constants.
In C and C++ the body of a
switch
statement can be a statement and statements withcase
labelsheads do not have to be immediately contained by that statement. Consider the simple loop:for (i = 0; i < n; ++i) foo();
where
n
is known to be positive. A trick known as Duff's device can be used in C or C++ to unroll the loop, but this is not valid code in the Java programming language:int q = (n+7)/8; switch (n%8) { case 0: do { foo(); // Great C hack, Tom, case 7: foo(); // but it's not valid here. case 6: foo(); case 5: foo(); case 4: foo(); case 3: foo(); case 2: foo(); case 1: foo(); } while (--q > 0); }
Fortunately, this trick does not seem to be widely known or used. Moreover, it is less needed nowadays; this sort of code transformation is properly in the province of state-of-the-art optimizing compilers.
14.11.2 The switch
Block of a switch
Statement
Given a switch
statement, all of the following must be true or a compile-time error occurs:
The switch block must be compatible with the type of the selector expression.
No two of the
case
constants associated with the switch block may have the same value.At most one
default
label is associated with the switch block.All switch rule expressions in the switch block must be statement expressions (14.8).
Switch rules in
switch
statements differ from those inswitch
expressions (15.28). Inswitch
statements, any switch rule expression must be a statement expression (14.8), whereas inswitch
expressions there is no such restriction (15.28.1).
14.11.3 Execution of a switch
statement
A switch
statement is executed by first evaluating the Expression. If the Expression evaluates to null
, a NullPointerException
is thrown and the entire switch
statement completes abruptly for that reason. Otherwise, if the result is of type Character
, Byte
, Short
, or Integer
, it is subject to unboxing conversion (5.1.8).
If evaluation of the Expression or the subsequent unboxing conversion (if any) completes abruptly for some reason, the switch
statement completes abruptly for the same reason.
Otherwise, execution continues by comparing the value of the Expression with each case
constant, and there is a choice:
If one of the
case
constants is equal to the value of the expression, then we say that thecase
label matches. Equality is defined in terms of the==
operator (15.21) unless the value of the expression is aString
, in which case equality is defined in terms of theequals
method of classString
.All statements after the matching
case
label in theswitch
block, if any, are executed in sequence.If all these statements complete normally, or if there are no statements after the matching
case
label, then the entireswitch
statement completes normally.If no
case
label matches but there is adefault
label, then all statements after thedefault
label in theswitch
block, if any, are executed in sequence.If all these statements complete normally, or if there are no statements after the
default
label, then the entireswitch
statement completes normally.If no
case
label matches and there is nodefault
label, then no further action is taken and theswitch
statement completes normally.
When the switch
statement is executed, first the selector expression is evaluated:
If evaluation of the selector expression completes abruptly for some reason, the
switch
statement completes abruptly for the same reason.If the selector expression evaluates to
null
, then aNullPointerException
is thrown and the entireswitch
statement completes abruptly for that reason.Otherwise, execution continues by determining if a switch label associated with the switch block matches the value of the selector expression:
If no switch label matches, then the entire
switch
statement completes normally.If a switch label matches, then one of the following applies:
If it is the switch label for a switch rule statement expression, then the statement expression is evaluated. If the result of evaluation is a value, it is discarded. If the evaluation completes normally, then the
switch
statement completes normally.If it is the switch label for a switch rule block, then the block is executed. If this block completes normally, then the
switch
statement completes normally.If it is the switch label for a switch rule
throw
statement, then thethrow
statement is executed.If it is the switch label for a statement group, then all the statements in the switch block that follow it are executed in order. If these statements complete normally, then the
switch
statement completes normally.Otherwise, there are no statements that follow it in the switch block, and the
switch
statement completes normally.
If any statement immediately contained by the Block body of the execution of any statement or expression completes abruptly, it is handled as follows:switch
statement
If execution of
the Statementa statement completes abruptly because of abreak
with no label, no further action is taken and theswitch
statement completes normally.If execution of
the Statementa statement or expression completes abruptly for any other reason, theswitch
statement completes abruptly for the same reason.
The case of abrupt completion because of a
yield
statement is handled by the general rule forswitch
expressions (15.28).
The case of abrupt completion because of a
break
with a label is handled by the general rule for labeled statements (14.7).
Example 14.11-1 14.11.3-1. Fall-Through in the switch
Statement
As in C and C++, execution of statements in a switch
block "falls through labels."
When a selector expression matches a switch label for a switch rule, the switch rule expression or statement introduced by the switch label is executed and nothing else. In the case of a switch label for a statement group, all the block statements in the switch block that follow the switch label are executed, including those that appear after subsequent switch labels. The effect is that, as in C and C++, execution of statements can "fall through labels."
For example, the program:
class TooMany {
static void howMany(int k) {
switch (k) {
case 1: System.out.print("one ");
case 2: System.out.print("too ");
case 3: System.out.println("many");
}
}
public static void main(String[] args) {
howMany(3);
howMany(2);
howMany(1);
}
}
contains a switch
block in which the code for each case
falls through into the code for the next case
. As a result, the program prints:
many
too many
one too many
This fall through behaviour can be the cause of subtle bugs. If code is not to fall through case
to case
in this manner, then switch rules can be used, or break
statements should be used, as in this examplebreak
statements can be used to explicitly indicate where control should be transfered, as follows:
class TwoMany {
static void howMany(int k) {
switch (k) {
case 1: System.out.println("one");
break; // exit the switch
case 2: System.out.println("two");
break; // exit the switch
case 3: System.out.println("many");
break; // not needed, but good style
}
}
public static void main(String[] args) {
howMany(1);
howMany(2);
howMany(3);
}
}
class TwoMany {
static void howManyRule(int k) {
switch (k) {
case 1 -> System.out.println("one");
case 2 -> System.out.println("two");
case 3 -> System.out.println("many");
}
}
static void howManyGroup(int k) {
switch (k) {
case 1: System.out.println("one");
break; // exit the switch
case 2: System.out.println("two");
break; // exit the switch
case 3: System.out.println("many");
break; // not needed, but good style
}
}
public static void main(String args) {
howManyRule(1);
howManyRule(2);
howManyRule(3);
howManyGroup(1);
howManyGroup(2);
howManyGroup(3);
}
}
This program prints:
one
two
many
one
two
many
one
two
many
14.15 The break
Statement
A break
statement transfers control out of an enclosing statement.
- BreakStatement:
break
[ Identifier ];
break
Identifier;
break
;
There are two kinds of break
statement:
A
break
statement with label Identifier.A
break
statement with no label.
A break
statement with no label attempts to transfer control to the innermost enclosing switch
, while
, do
, or for
statement of the immediately enclosing method or initializer; this statement, which is called the break target, then immediately completes normally.
To be precise, a break
statement with no label always completes abruptly, the reason being a break
with no label.
If no switch
, while
, do
, or for
statement in the immediately enclosing method, constructor, or initializer contains the break
statement, a compile-time error occurs.
A break
statement with label Identifier attempts to transfer control to the enclosing labeled statement (14.7) that has the same Identifier as its label; this statement, which is called the break target, then immediately completes normally. In this case, the break target need not be a switch
, while
, do
, or for
statement.
To be precise, a break
statement with label Identifier always completes abruptly, the reason being a break
with label Identifier.
A break
statement must refer to a label within the immediately enclosing method, constructor, initializer, or lambda body. There are no non-local jumps. If no labeled statement with Identifier as its label in the immediately enclosing method, constructor, initializer, or lambda body contains the break
statement, a compile-time error occurs.
It is a compile-time error if a break
statement has no break target.
It is a compile-time error if the break target of a break
statement contains any method, constructor, initializer, lambda expression, or switch
expression that encloses the break
statement.
Execution of an break
statement with no label always completes abruptly, the reason being a break with no label.
Execution of a break
statement with label Identifier always completes abruptly, the reason being a break with label Identifier.
It can be seen, then, that a
break
statement always completes abruptly.
The preceding descriptions say "attempts to transfer control" rather than just "transfers control" because if there are any
try
statements (14.20) within the break target whosetry
blocks orcatch
clauses contain thebreak
statement, then anyfinally
clauses of thosetry
statements are executed, in order, innermost to outermost, before control is transferred to the break target. Abrupt completion of afinally
clause can disrupt the transfer of control initiated by abreak
statement.
Example 14.15-1. The break
Statement
In the following example, a mathematical graph is represented by an array of arrays. A graph consists of a set of nodes and a set of edges; each edge is an arrow that points from some node to some other node, or from a node to itself. In this example it is assumed that there are no redundant edges; that is, for any two nodes P and Q, where Q may be the same as P, there is at most one edge from P to Q.
Nodes are represented by integers, and there is an edge from node i to node edges[*i*][*j*]
for every i and j for which the array reference edges[*i*][*j*]
does not throw an ArrayIndexOutOfBoundsException
.
The task of the method loseEdges
, given integers i and j, is to construct a new graph by copying a given graph but omitting the edge from node i to node j, if any, and the edge from node j to node i, if any:
class Graph {
int edges[][];
public Graph(int[][] edges) { this.edges = edges; }
public Graph loseEdges(int i, int j) {
int n = edges.length;
int[][] newedges = new int[n][];
for (int k = 0; k < n; ++k) {
edgelist:
{
int z;
search:
{
if (k == i) {
for (z = 0; z < edges[k].length; ++z) {
if (edges[k][z] == j) break search;
}
} else if (k == j) {
for (z = 0; z < edges[k].length; ++z) {
if (edges[k][z] == i) break search;
}
}
// No edge to be deleted; share this list.
newedges[k] = edges[k];
break edgelist;
} //search
// Copy the list, omitting the edge at position z.
int m = edges[k].length - 1;
int ne[] = new int[m];
System.arraycopy(edges[k], 0, ne, 0, z);
System.arraycopy(edges[k], z+1, ne, z, m-z);
newedges[k] = ne;
} //edgelist
}
return new Graph(newedges);
}
}
Note the use of two statement labels, edgelist
and search
, and the use of break
statements. This allows the code that copies a list, omitting one edge, to be shared between two separate tests, the test for an edge from node i to node j, and the test for an edge from node j to node i.
14.16 The continue
Statement
A continue
statement may occur only in a while
, do
, or for
statement; statements of these three kinds are called iteration statements. Control passes to the loop-continuation point of an iteration statement.
- ContinueStatement:
continue
[ Identifier ];
continue
Identifier;
continue
;
There are two kinds of continue
statement:
A
continue
statement with label Identifier.A
continue
statement with no label.
A continue
statement with no label attempts to transfer control to the innermost enclosing while
, do
, or for
statement of the immediately enclosing method, constructor, or initializer; this statement, which is called the continue target, then immediately ends the current iteration and begins a new one.
To be precise, such a continue
statement always completes abruptly, the reason being a continue
with no label.
If no while
, do
, or for
statement of the immediately enclosing method, constructor, or initializer contains the continue
statement, a compile-time error occurs.
A continue
statement with label Identifier attempts to transfer control to the enclosing labeled statement (14.7) that has the same Identifier as its label; that statement, which is called the continue target, then immediately ends the current iteration and begins a new one.
To be precise, a continue
statement with label Identifier always completes abruptly, the reason being a continue
with label Identifier.
The continue target of a continue
statement with label must be a while
, do
, or for
statement, or a compile-time error occurs.
A continue
statement must refer to a label within the immediately enclosing method, constructor, initializer, or lambda body. There are no non-local jumps. If no labeled statement with Identifier as its label in the immediately enclosing method, constructor, initializer, or lambda body contains the continue
statement, a compile-time error occurs.
It is a compile-time error if a continue
statement has no continue target.
It is a compile-time error if the continue target contains any method, constructor, initializer, lambda expression, or switch
expression that contains the continue
statement.
Execution of an continue
statement with no label always completes abruptly, the reason being a continue with no label.
Execution of a continue
statement with label Identifier always completes abruptly, the reason being a continue with label Identifier.
It can be seen, then, that a
continue
statement always completes abruptly.
See the descriptions of the
while
statement (14.12),do
statement (14.13), andfor
statement (14.14) for a discussion of the handling of abrupt termination because ofcontinue
.
The preceding descriptions say "attempts to transfer control" rather than just "transfers control" because if there are any
try
statements (14.20) within the continue target whosetry
blocks orcatch
clauses contain thecontinue
statement, then anyfinally
clauses of thosetry
statements are executed, in order, innermost to outermost, before control is transferred to the continue target. Abrupt completion of afinally
clause can disrupt the transfer of control initiated by acontinue
statement.
Example 14.16-1. The continue
Statement
In the Graph
class in 14.15, one of the break
statements is used to finish execution of the entire body of the outermost for
loop. This break can be replaced by a continue
if the for
loop itself is labeled:
class Graph {
int edges[][];
public Graph(int[][] edges) { this.edges = edges; }
public Graph loseEdges(int i, int j) {
int n = edges.length;
int[][] newedges = new int[n][];
edgelists:
for (int k = 0; k < n; ++k) {
int z;
search:
{
if (k == i) {
for (z = 0; z < edges[k].length; ++z) {
if (edges[k][z] == j) break search;
}
} else if (k == j) {
for (z = 0; z < edges[k].length; ++z) {
if (edges[k][z] == i) break search;
}
}
// No edge to be deleted; share this list.
newedges[k] = edges[k];
continue edgelists;
} //search
// Copy the list, omitting the edge at position z.
int m = edges[k].length - 1;
int ne[] = new int[m];
System.arraycopy(edges[k], 0, ne, 0, z);
System.arraycopy(edges[k], z+1, ne, z, m-z);
newedges[k] = ne;
} //edgelists
return new Graph(newedges);
}
}
Which to use, if either, is largely a matter of programming style.
14.17 The return
Statement
A return
statement returns control to the invoker of a method (8.4, 15.12) or constructor (8.8, 15.9).
- ReturnStatement:
return
[ Expression ];
return
Exression;
return
;
There are two kinds of return
statement:
A
return
statement with value Expression.A
return
statement with no value.
A return
statement is contained in the innermost constructor, method, initializer, or lambda expression whose body encloses the return
statement.
A return
statement attempts to transfer control to the invoker of the innermost enclosing constructor, method, or lambda expression; this declaration is called the return target. In the case of a return statement with value Expression, the value of the Expression becomes the value of the invocation.
It is a compile-time error if a return
statement has no return target.
It is a compile-time error if the return target contains any initializer, or switch
expression that encloses the return
statement.
It is a compile-time error if a return
statement is contained in an instance initializer or a static initializer (8.6, 8.7).
A return
statement with no Expression must be contained in one of the following, or a compile-time error occurs:
A method that is declared, using the keywordvoid
, not to return a value (8.4.5)A constructor (8.8.7)A lambda expression (15.27)
It is a compile-time error if the return target of a return
statement with no value is a method, and that method is not declared 'void'.
A return
statement with no Expression attempts to transfer control to the invoker of the method, constructor, or lambda body that contains it. To be precise, a return
statement with no Expression always completes abruptly, the reason being a return with no value.
A return
statement with an Expression must be contained in one of the following, or a compile-time error occurs:
A method that is declared to return a valueA lambda expression
The Expression must denote a variable or a value, or a compile-time error occurs.
When a return
statement with an Expression appears in a method declaration, the Expression must be assignable (5.2) to the declared return type of the method, or a compile-time error occurs.
It is a compile-time error if the return target of a return
statement with an Expression is a constructor or a method that is declared void
.
It is a compile-time error if the return target of a return
statement with an Expression is a method with declared return type T, and Expression is not assignable (5.2) to T.
Execution of a return
statement with no value always completes abruptly, the reason being a return with no value.
A Execution of a return
statement with an Expression attempts to transfer control to the invoker of the method or lambda body that contains it; the value of the Expression becomes the value of the method invocation. More precisely, execution of such a return
statement first evaluates the Expression.return
statement with an Expression first evaluates the Expression. If the evaluation of the Expression completes abruptly for some reason, then the return
statement completes abruptly for that reason. If evaluation of the Expression completes normally, producing a value V, then the return
statement completes abruptly, the reason being a return with value V.
If the expression is of type float
and is not FP-strict (15.4), then the value may be an element of either the float value set or the float-extended-exponent value set (4.2.3). If the expression is of type double
and is not FP-strict, then the value may be an element of either the double value set or the double-extended-exponent value set.
It can be seen, then, that a
return
statement always completes abruptly.
The preceding descriptions say "attempts to transfer control" rather than just "transfers control" because if there are any
try
statements (14.20) within the method or constructor whosetry
blocks orcatch
clauses contain thereturn
statement, then anyfinally
clauses of thosetry
statements will be executed, in order, innermost to outermost, before control is transferred to the invoker of the method or constructor. Abrupt completion of afinally
clause can disrupt the transfer of control initiated by areturn
statement.
14.21 The yield
Statement
This replaces the current section 14.21, which is renumbered as 14.22.
A yield
statement transfers control by causing an enclosing switch
expression to produce a specified value.
- YieldStatement:
yield
Expression;
A yield
statement attempts to transfer control to the innermost enclosing switch
expression; this expression, which is called the yield target, then immediately completes normally and the value of the Expression becomes the value of the switch
expression.
It is a compile-time error if a yield
statement has no yield target.
It is a compile-time error if the yield target contains any method, constructor, initializer, or lambda expression that encloses the yield
statement.
It is a compile-time error if the Expression of a yield
statement is void (15.1).
Execution of a yield
statement first evaluates the Expression. If the evaluation of the Expression completes abruptly for some reason, then the yield
statement completes abruptly for that reason. If evaluation of the Expression completes normally, producing a value V, then the yield
statement completes abruptly, the reason being a yield with value V.
It can be seen that, like a
break
statement, ayield
statement always completes abruptly.
Example 14.15-2. The yield
Statement
In the following example, a yield
statement is used to produce a value for the enclosing switch
expression.
class Enum {
enum Day {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY
}
public static int calculate(Day d) {
return switch (d) {
case MONDAY -> 0;
case TUESDAY -> 1;
default -> {
int l = d.toString().length();
yield l*l; // Value of switch expression
}
};
}
}
14.21 14.22 Unreachable Statements
This section has been renumbered from 14.21 to 14.22.
It is a compile-time error if a statement cannot be executed because it is unreachable.
This section is devoted to a precise explanation of the word "reachable." The idea is that there must be some possible execution path from the beginning of the constructor, method, instance initializer, or static initializer that contains the statement to the statement itself. The analysis takes into account the structure of statements. Except for the special treatment of
while
,do
, andfor
statements whose condition expression has the constant valuetrue
, the values of expressions are not taken into account in the flow analysis.
For example, a Java compiler will accept the code:
{ int n = 5; while (n > 7) k = 2; }
even though the value of
n
is known at compile time and in principle it can be known at compile time that the assignment tok
can never be executed.
The rules in this section define two technical terms:
whether a statement is reachable
whether a statement can complete normally
The definitions here allow a statement to complete normally only if it is reachable.
To shorten the description of the rules, the customary abbreviation "iff" is used to mean "if and only if."
A reachable break
statement exits a statement if, within the break target, either there are no try
statements whose try
blocks contain the break
statement, or there are try
statements whose try
blocks contain the break
statement and all finally
clauses of those try
statements can complete normally.
This definition is based on the logic around "attempts to transfer control" in 14.15.
A continue
statement continues a do
statement if, within the do
statement, either there are no try
statements whose try
blocks contain the continue
statement, or there are try
statements whose try
blocks contain the continue
statement and all finally
clauses of those try
statements can complete normally.
The rules are as follows:
The block that is the body of a constructor, method, instance initializer,
or static initializerstatic initializer, lambda expression, orswitch
expression is reachable.An empty block that is not a switch block can complete normally iff it is reachable.
A non-empty block that is not a switch block can complete normally iff the last statement in it can complete normally.
The first statement in a non-empty block that is not a switch block is reachable iff the block is reachable.
Every other statement S in a non-empty block that is not a switch block is reachable iff the statement preceding S can complete normally.
A local class declaration statement can complete normally iff it is reachable.
A local variable declaration statement can complete normally iff it is reachable.
An empty statement can complete normally iff it is reachable.
A labeled statement can complete normally if at least one of the following is true:
The contained statement can complete normally.
There is a reachable
break
statement that exits the labeled statement.
The contained statement is reachable iff the labeled statement is reachable.
An expression statement can complete normally iff it is reachable.
An
if
-then
statement can complete normally iff it is reachable.The
then
-statement is reachable iff theif
-then
statement is reachable.An
if
-then
-else
statement can complete normally iff thethen
-statement can complete normally or theelse
-statement can complete normally.The
then
-statement is reachable iff theif
-then
-else
statement is reachable.The
else
-statement is reachable iff theif
-then
-else
statement is reachable.This handling of an
if
statement, whether or not it has anelse
part, is rather unusual. The rationale is given at the end of this section.An
assert
statement can complete normally iff it is reachable.A
switch
statement whose switch block is empty, or contains only switch labels, completes normally.A
switch
statement with a switch block that consists of switch labeled statement groups can complete normally iff at least one of the following is true:The switch block is empty or contains only switch labels.The last statement in the switch block can complete normally.
There is at least one switch label after the last switch block statement group.
The switch block does not contain a
default
label.There is a reachable
break
statement that exits theswitch
statement.
A
switch
statement with a switch block that consists of switch rules can complete normally iff at least one of the following is true:One of the switch rules introduces a switch rule expression (which is necessarily a statement expression).
One of the switch rules introduces a switch rule block that can complete normally.
One of the switch rules introduces a switch rule block that contains a reachable
break
statement that exits theswitch
statement.
A switch block is reachable iff its
switch
statement is reachable.A statement in a switch block that consists of switch labeled statement groups is reachable iff
itsthe switch block is reachable and at least one of the following is true:switch
statementIt bears a
case
ordefault
label.There is a statement preceding it in the switch block and that preceding statement can complete normally.
A switch rule block in a switch block is reachable iff the switch block is reachable.
A switch rule
throw
statement in a switch block is reachable iff the switch block is reachable.A
while
statement can complete normally iff at least one of the following is true:The
while
statement is reachable and the condition expression is not a constant expression (15.2815.29) with valuetrue
.There is a reachable
break
statement that exits thewhile
statement.
The contained statement is reachable iff the
while
statement is reachable and the condition expression is not a constant expression whose value isfalse
.A
do
statement can complete normally iff at least one of the following is true:The contained statement can complete normally and the condition expression is not a constant expression (
15.2815.29) with valuetrue
.The
do
statement contains a reachablecontinue
statement with no label, and thedo
statement is the innermostwhile
,do
, orfor
statement that contains thatcontinue
statement, and thecontinue
statement continues thatdo
statement, and the condition expression is not a constant expression with valuetrue
.The
do
statement contains a reachablecontinue
statement with a label L, and thedo
statement has label L, and thecontinue
statement continues thatdo
statement, and the condition expression is not a constant expression with valuetrue
.There is a reachable
break
statement that exits thedo
statement.
The contained statement is reachable iff the
do
statement is reachable.A basic
for
statement can complete normally iff at least one of the following is true:The
for
statement is reachable, there is a condition expression, and the condition expression is not a constant expression (15.2815.29) with valuetrue
.There is a reachable
break
statement that exits thefor
statement.
The contained statement is reachable iff the
for
statement is reachable and the condition expression is not a constant expression whose value isfalse
.An enhanced
for
statement can complete normally iff it is reachable.A
break
,yield
,continue
,return
, orthrow
statement cannot complete normally.A
synchronized
statement can complete normally iff the contained statement can complete normally.The contained statement is reachable iff the
synchronized
statement is reachable.A
try
statement can complete normally iff both of the following are true:The
try
block can complete normally or anycatch
block can complete normally.If the
try
statement has afinally
block, then thefinally
block can complete normally.
The
try
block is reachable iff thetry
statement is reachable.A
catch
block C is reachable iff both of the following are true:Either the type of C's parameter is an unchecked exception type or
Exception
or a superclass ofException
, or some expression orthrow
statement in thetry
block is reachable and can throw a checked exception whose type is assignable to the type of C's parameter. (An expression is reachable iff the innermost statement containing it is reachable.)See 15.6 for normal and abrupt completion of expressions.
There is no earlier
catch
block A in thetry
statement such that the type of C's parameter is the same as or a subclass of the type of A's parameter.
The Block of a
catch
block is reachable iff thecatch
block is reachable.If a
finally
block is present, it is reachable iff thetry
statement is reachable.
One might expect the
if
statement to be handled in the following manner:
An
if
-then
statement can complete normally iff at least one of the following is true:
The
if
-then
statement is reachable and the condition expression is not a constant expression whose value istrue
.The
then
-statement can complete normally.The
then
-statement is reachable iff theif
-then
statement is reachable and the condition expression is not a constant expression whose value isfalse
.An
if
-then
-else
statement can complete normally iff thethen
-statement can complete normally or theelse
-statement can complete normally.The
then
-statement is reachable iff theif
-then
-else
statement is reachable and the condition expression is not a constant expression whose value isfalse
.The
else
-statement is reachable iff theif
-then
-else
statement is reachable and the condition expression is not a constant expression whose value istrue
.
This approach would be consistent with the treatment of other control structures. However, in order to allow the
if
statement to be used conveniently for "conditional compilation" purposes, the actual rules differ.
As an example, the following statement results in a compile-time error:
while (false) { x=3; }
because the statement
x=3;
is not reachable; but the superficially similar case:if (false) { x=3; }
does not result in a compile-time error. An optimizing compiler may realize that the statement
x=3;
will never be executed and may choose to omit the code for that statement from the generatedclass
file, but the statementx=3;
is not regarded as "unreachable" in the technical sense specified here.
The rationale for this differing treatment is to allow programmers to define "flag" variables such as:
static final boolean DEBUG = false;
and then write code such as:
if (DEBUG) { x=3; }
The idea is that it should be possible to change the value of
DEBUG
fromfalse
totrue
or fromtrue
tofalse
and then compile the code correctly with no other changes to the program text.
Conditional compilation comes with a caveat. If a set of classes that use a "flag" variable - or more precisely, any
static
constant variable (4.12.4) - are compiled and conditional code is omitted, it does not suffice later to distribute just a new version of the class or interface that contains the definition of the flag. The classes that use the flag will not see its new value, so their behavior may be surprising. In essence, a change to the value of a flag is binary compatible with pre-existing binaries (noLinkageError
occurs) but not behaviorally compatible.
Another reason for "inlining" values of
static
constant variables is because ofswitch
statements. They are the only kind of statement that relies on constant expressions, namely that eachcase
label of aswitch
statement must be a constant expression whose value is different than every othercase
label.case
labels are often references tostatic
constant variables so it may not be immediately obvious that all the labels have different values. If it is proven that there are no duplicate labels at compile time, then inlining the values into theclass
file ensures there are no duplicate labels at run time either - a very desirable property.
Example 14.21-1. Conditional Compilation
If the example:
class Flags { static final boolean DEBUG = true; }
class Test {
public static void main(String[] args) {
if (Flags.DEBUG)
System.out.println("DEBUG is true");
}
}
is compiled and executed, it produces the output:
DEBUG is true
Suppose that a new version of class Flags
is produced:
class Flags { static final boolean DEBUG = false; }
If Flags
is recompiled but not Test
, then running the new binary with the existing binary of Test
produces the output:
DEBUG is true
because DEBUG
is a static
constant variable, so its value could have been used in compiling Test
without making a reference to the class Flags
.
This behavior would also occur if Flags
was an interface, as in the modified example:
interface Flags { boolean DEBUG = true; }
class Test {
public static void main(String[] args) {
if (Flags.DEBUG)
System.out.println("DEBUG is true");
}
}
In fact, because the fields of interfaces are always static
and final
, we recommend that only constant expressions be assigned to fields of interfaces. We note, but do not recommend, that if a field of primitive type of an interface may change, its value may be expressed idiomatically as in:
interface Flags {
boolean debug = Boolean.valueOf(true).booleanValue();
}
ensuring that this value is not a constant expression. Similar idioms exist for the other primitive types.
Chapter 15: Expressions
15.1 Evaluation, Denotation, and Result
When an expression in a program is evaluated (executed), the result denotes one of three things:
A variable (4.12) (in C, this would be called an lvalue)
Nothing (the expression is said to be void)
If an expression denotes a variable, and a value is required for use in further evaluation, then the value of that variable is used. In this context, if the expression denotes a variable or a value, we may speak simply of the value of the expression.
Value set conversion (5.1.13) is applied to the result of every expression that produces a value, including when the value of a variable of type float
or double
is used.
An expression denotes nothing if and only if it is a method invocation (15.12) that invokes a method that does not return a value, that is, a method declared void
(8.4). Such an expression can be used only as an expression statement (14.8) or as the single expression of a lambda body (15.27.2), because every other context in which an expression can appear requires the expression to denote something. An expression statement or lambda body that is a method invocation may also invoke a method that produces a result; in this case the value returned by the method is quietly discarded.
Evaluation of an expression can produce side effects, because expressions may contain embedded assignments, increment operators, decrement operators, and method invocations, as well as statements contained in switch
expressions.
An expression occurs in either:
The declaration of some (class or interface) type that is being declared: in a field initializer, in a static initializer, in an instance initializer, in a constructor declaration, in a method declaration, or in an annotation.
An annotation on a package declaration or on a top level type declaration.
15.2 Forms of Expressions
Expressions can be broadly categorized into one of the following syntactic forms:
Precedence among operators is managed by a hierarchy of grammar productions. The lowest precedence operator is the arrow of a lambda expression (->
), followed by the assignment operators. Thus, all expressions are syntactically included in the LambdaExpression and AssignmentExpression nonterminals:
- Expression:
- LambdaExpression
- AssignmentExpression
When some expressions appear in certain contexts, they are considered poly expressions. The following forms of expressions may be poly expressions:
Parenthesized expressions (15.8.5)
Class instance creation expressions (15.9)
Method invocation expressions (15.12)
Method reference expressions (15.13)
Conditional expressions (15.25)
Lambda expressions (15.27)
switch
expressions (15.28)
The rules determining whether an expression of one of these forms is a poly expression are given in the individual sections that specify these forms of expressions.
Expressions that are not poly expressions are standalone expressions. Standalone expressions are expressions of the forms above when determined not to be poly expressions, as well as all expressions of all other forms. Expressions of all other forms are said to have a standalone form.
Some expressions have a value that can be determined at compile time. These are constant expressions (15.28 15.29).
15.6 Normal and Abrupt Completion of Evaluation
Every expression has a normal mode of evaluation in which certain computational steps are carried out. The following sections describe the normal mode of evaluation for each kind of expression.
If all the steps are carried out without an exception being thrown, the expression is said to complete normally.
If, however, evaluation of an expression throws an exception, then the expression is said to complete abruptly. An abrupt completion always has an associated reason, which is always a throw
with a given value.
Run-time exceptions are thrown by the predefined operators as follows:
A class instance creation expression (15.9.4), array creation expression (15.10.2), method reference expression (15.13.3), array initializer expression (10.6), string concatenation operator expression (15.18.1), or lambda expression (15.27.4) throws an
OutOfMemoryError
if there is insufficient memory available.An array creation expression (15.10.2) throws a
NegativeArraySizeException
if the value of any dimension expression is less than zero.An array access expression (15.10.4) throws a
NullPointerException
if the value of the array reference expression isnull
.An array access expression (15.10.4) throws an
ArrayIndexOutOfBoundsException
if the value of the array index expression is negative or greater than or equal to thelength
of the array.A field access expression (15.11) throws a
NullPointerException
if the value of the object reference expression isnull
.A method invocation expression (15.12) that invokes an instance method throws a
NullPointerException
if the target reference isnull
.A cast expression (15.16) throws a
ClassCastException
if a cast is found to be impermissible at run time.An integer division (15.17.2) or integer remainder (15.17.3) operator throws an
ArithmeticException
if the value of the right-hand operand expression is zero.An assignment to an array component of reference type (15.26.1), a method invocation expression (15.12), or a prefix or postfix increment (15.14.2, 15.15.1) or decrement operator (15.14.3, 15.15.2) may all throw an
OutOfMemoryError
as a result of boxing conversion (5.1.7).An assignment to an array component of reference type (15.26.1) throws an
ArrayStoreException
when the value to be assigned is not compatible with the component type of the array (10.5).A switch expression (15.28) may throw an
IncompatibleClassChangeError
if no switch label matches the value of the selector expression.
A method invocation expression can also result in an exception being thrown if an exception occurs that causes execution of the method body to complete abruptly.
A class instance creation expression can also result in an exception being thrown if an exception occurs that causes execution of the constructor to complete abruptly.
Various linkage and virtual machine errors may also occur during the evaluation of an expression. By their nature, such errors are difficult to predict and difficult to handle.
If an exception occurs, then evaluation of one or more expressions may be terminated before all steps of their normal mode of evaluation are complete; such expressions are said to complete abruptly.
If evaluation of an expression requires evaluation of a subexpression, then abrupt completion of the subexpression always causes the immediate abrupt completion of the expression itself, with the same reason, and all succeeding steps in the normal mode of evaluation are not performed.
The terms "complete normally" and "complete abruptly" are also applied to the execution of statements (14.1). A statement may complete abruptly for a variety of reasons, not just because an exception is thrown.
15.12 Method Invocation Expressions
15.12.2 Compile-Time Step 2: Determine Method Signature
15.12.2.1 Identify Potentially Applicable Methods
The class or interface determined by compile-time step 1 (15.12.1) is searched for all member methods that are potentially applicable to this method invocation; members inherited from superclasses and superinterfaces are included in this search.
In addition, if the form of the method invocation expression is MethodName - that is, a single Identifier - then the search for potentially applicable methods also examines all member methods that are imported by single-static-import declarations and static-import-on-demand declarations of the compilation unit where the method invocation occurs (7.5.3, 7.5.4) and that are not shadowed at the point where the method invocation appears.
A member method is potentially applicable to a method invocation if and only if all of the following are true:
The name of the member is identical to the name of the method in the method invocation.
The member is accessible (6.6) to the class or interface in which the method invocation appears.
Whether a member method is accessible at a method invocation depends on the access modifier (
public
,protected
, no modifier (package access), orprivate
) in the member's declaration, and on the inheritance of the member by the class or interface determined by compile-time step 1, and on where the method invocation appears.If the member is a fixed arity method with arity n, the arity of the method invocation is equal to n, and for all i (1 ≤ i ≤ n), the i'th argument of the method invocation is potentially compatible, as defined below, with the type of the i'th parameter of the method.
If the member is a variable arity method with arity n, then for all i (1 ≤ i ≤ n-1), the i'th argument of the method invocation is potentially compatible with the type of the i'th parameter of the method; and, where the nth parameter of the method has type T
[]
, one of the following is true:The arity of the method invocation is equal to n-1.
The arity of the method invocation is equal to n, and the nth argument of the method invocation is potentially compatible with either T or T
[]
.The arity of the method invocation is m, where m > n, and for all i (n ≤ i ≤ m), the i'th argument of the method invocation is potentially compatible with T.
If the method invocation includes explicit type arguments, and the member is a generic method, then the number of type arguments is equal to the number of type parameters of the method.
This clause implies that a non-generic method may be potentially applicable to an invocation that supplies explicit type arguments. Indeed, it may turn out to be applicable. In such a case, the type arguments will simply be ignored.
This rule stems from issues of compatibility and principles of substitutability. Since interfaces or superclasses may be generified independently of their subtypes, we may override a generic method with a non-generic one. However, the overriding (non-generic) method must be applicable to calls to the generic method, including calls that explicitly pass type arguments. Otherwise the subtype would not be substitutable for its generified supertype.
If the search does not yield at least one method that is potentially applicable, then a compile-time error occurs.
An expression is potentially compatible with a target type according to the following rules:
A lambda expression (15.27) is potentially compatible with a functional interface type T (9.8) if all of the following are true:
The arity of the function type of T (9.9) is the same as the arity of the lambda expression.
If the function type of T has a
void
return, then the lambda body is either a statement expression (14.8) or a void-compatible block (15.27.2).If the function type of T has a (non-
void
) return type, then the lambda body is either an expression or a value-compatible block (15.27.2).
A method reference expression (15.13) is potentially compatible with a functional interface type T if, where the arity of the function type of T is n, there exists at least one potentially applicable method when the method reference expression targets the function type with arity n (15.13.1), and one of the following is true:
The method reference expression has the form ReferenceType
::
[TypeArguments] Identifier and at least one potentially applicable method is either (i)static
and supports arity n, or (ii) notstatic
and supports arity n-1.The method reference expression has some other form and at least one potentially applicable method is not
static
.
A lambda expression or a method reference expression is potentially compatible with a type variable if the type variable is a type parameter of the candidate method.
A parenthesized expression (15.8.5) is potentially compatible with a type if its contained expression is potentially compatible with that type.
A conditional expression (15.25) is potentially compatible with a type if each of its second and third operand expressions are potentially compatible with that type.
A
switch
expression (15.28) is potentially compatible with a type if all of its result expressions are potentially compatible with that type.A class instance creation expression, a method invocation expression, or an expression of a standalone form (15.2) is potentially compatible with any type.
The definition of potential applicability goes beyond a basic arity check to also take into account the presence and "shape" of functional interface target types. In some cases involving type argument inference, a lambda expression appearing as a method invocation argument cannot be properly typed until after overload resolution. These rules allow the form of the lambda expression to still be taken into account, discarding obviously incorrect target types that might otherwise cause ambiguity errors.
15.12.2.5 Choosing the Most Specific Method
If more than one member method is both accessible and applicable to a method invocation, it is necessary to choose one to provide the descriptor for the run-time method dispatch. The Java programming language uses the rule that the most specific method is chosen.
The informal intuition is that one method is more specific than another if any invocation handled by the first method could be passed on to the other one without a compile-time error. In cases such as an explicitly typed lambda expression argument (15.27.1) or a variable arity invocation (15.12.2.4), some flexibility is allowed to adapt one signature to the other.
One applicable method m1 is more specific than another applicable method m2, for an invocation with argument expressions e1, ..., ek, if any of the following are true:
m2 is generic, and m1 is inferred to be more specific than m2 for argument expressions e1, ..., ek by 18.5.4.
m2 is not generic, and m1 and m2 are applicable by strict or loose invocation, and where m1 has formal parameter types S1, ..., Sn and m2 has formal parameter types T1, ..., Tn, the type Si is more specific than Ti for argument ei for all i (1 ≤ i ≤ n, n = k).
m2 is not generic, and m1 and m2 are applicable by variable arity invocation, and where the first k variable arity parameter types of m1 are S1, ..., Sk and the first k variable arity parameter types of m2 are T1, ..., Tk, the type Si is more specific than Ti for argument ei for all i (1 ≤ i ≤ k). Additionally, if m2 has k+1 parameters, then the k+1'th variable arity parameter type of m1 is a subtype of the k+1'th variable arity parameter type of m2.
The above conditions are the only circumstances under which one method may be more specific than another.
A type S is more specific than a type T for any expression if S <:
T (4.10).
A functional interface type S is more specific than a functional interface type T for an expression e if all of the following are true:
The interface of S is neither a superinterface nor a subinterface of the interface of T.
If S or T is an intersection type, it is not the case that any interface of S is a superinterface or a subinterface of any interface of T. (The "interfaces of" an intersection type refers here to the set of interfaces that appear as (possibly parameterized) interface types in the intersection.)
Let MTS be the function type of the capture of S, and let MTT be the function type of T. MTS and MTT must have the same type parameters (if any) (8.4.4).
Let P1, ..., Pn be the formal parameter types of MTS, adapted to the type parameters of MTT. Let P1', ..., Pn' be the formal parameter types of the function type of S (without capture), adapted to the type parameters of MTT. Let Q1, ..., Qn be the formal parameter types of MTT. Then, for all i (1 ≤ i ≤ n), Qi
<:
Pi and Qi = Pi'.Generally, this rule asserts that the formal parameter types derived from S and T are the same. But in the case in which S is a wildcard-parameterized type, the check is more complex in order to allow capture variables to occur in formal parameter types: first, each formal parameter type of T must be a subtype of the corresponding formal parameter type of the capture of S; second, after mapping the wildcards to their bounds (9.9), the formal parameter types of the resulting function types are the same.
Let RS be the return type of MTS, adapted to the type parameters of MTT, and let RT be the return type of MTT. One of the following must be true:
e is an explicitly typed lambda expression (15.27.1), and one of the following is true:
RT is
void
.RS
<:
RT.RS and RT are functional interface types, and there is at least one result expression, and RS is more specific than RT for each result expression of e.
The result expression of a lambda expression with a block body is defined in 15.27.2; the result expression of a lambda expression with an expression body is simply the body itself.
RS is a primitive type, and RT is a reference type, and there is at least one result expression, and each result expression of e is a standalone expression (15.2) of a primitive type.
RS is a reference type, and RT is a primitive type, and there is at least one result expression, and each result expression of e is either a standalone expression of a reference type or a poly expression.
e is an exact method reference expression (15.13.1), and one of the following is true:
RT is
void
.RS
<:
RT.RS is a primitive type, RT is a reference type, and the compile-time declaration for the method reference has a return type which is a primitive type.
RS is a reference type, RT is a primitive type, and the compile-time declaration for the method reference has a return type which is a reference type.
e is a parenthesized expression, and one of these conditions applies recursively to the contained expression.
e is a conditional expression, and, for each of the second and third operands, one of these conditions applies recursively.
e is a
switch
expression, and, for each of its result expressions, one of these conditions applies recursively.
A method m1 is strictly more specific than another method m2 if and only if m1 is more specific than m2 and m2 is not more specific than m1.
A method is said to be maximally specific for a method invocation if it is accessible and applicable and there is no other method that is accessible and applicable that is strictly more specific.
If there is exactly one maximally specific method, then that method is in fact the most specific method; it is necessarily more specific than any other accessible method that is applicable. It is then subjected to some further compile-time checks as specified in 15.12.3.
It is possible that no method is the most specific, because there are two or more methods that are maximally specific. In this case:
If all the maximally specific methods have override-equivalent signatures (8.4.2), and exactly one of the maximally specific methods is concrete (that is, neither
abstract
nor default), then it is the most specific method.Otherwise, if all the maximally specific methods have override-equivalent signatures, and all the maximally specific methods are
abstract
or default, and the declarations of these methods have the same erased parameter types, and at least one maximally specific method is preferred according to the rules below, then the most specific method is chosen arbitrarily among the subset of the maximally specific methods that are preferred. The most specific method is then considered to beabstract
.A maximally specific method is preferred if it has:
a signature that is a subsignature of every maximally specific method's signature; and
a return type R (possibly
void
), where either R is the same as every maximally specific method's return type, or R is a reference type and is a subtype of every maximally specific method's return type (after adapting for any type parameters (8.4.4) if the two methods have the same signature).
If no preferred method exists according to the above rules, then a maximally specific method is preferred if it:
has a signature that is a subsignature of every maximally specific method's signature; and
is return-type-substitutable (8.4.5) for every maximally specific method.
The thrown exception types of the most specific method are derived from the
throws
clauses of the maximally specific methods, as follows:If the most specific method is generic, the
throws
clauses are first adapted to the type parameters of the most specific method (8.4.4).If the most specific method is not generic but at least one maximally specific method is generic, the
throws
clauses are first erased.Then, the thrown exception types include every type E which satisfies the following constraints:
E is mentioned in one of the
throws
clauses.For each
throws
clause, E is a subtype of some type named in that clause.
These rules for deriving a single method type from a group of overloaded methods are also used to identify the function type of a functional interface (9.9).
Otherwise, the method invocation is ambiguous, and a compile-time error occurs.
15.15 Unary Operators Expressions
The operators +
, -
, ++
, --
, ~
, !
, and the cast operator (15.16) are called the unary operators, which are used to form unary expressions. In addition, the switch
expression (15.28) is treated grammatically as a unary expression.
- UnaryExpression:
- PreIncrementExpression
- PreDecrementExpression
+
UnaryExpression-
UnaryExpression- UnaryExpressionNotPlusMinus
- PreIncrementExpression:
++
UnaryExpression- PreDecrementExpression:
--
UnaryExpression- UnaryExpressionNotPlusMinus:
- PostfixExpression
~
UnaryExpression!
UnaryExpression- CastExpression
- SwitchExpression
The following production from 15.16 is shown here for convenience:
CastExpression:(
PrimitiveType)
UnaryExpression(
ReferenceType {AdditionalBound})
UnaryExpressionNotPlusMinus(
ReferenceType {AdditionalBound})
LambdaExpression
Expressions with unary operators group right-to-left, so that -~x
means the same as -(~x)
.
This portion of the grammar contains some tricks to avoid two potential syntactic ambiguities.
The first potential ambiguity would arise in expressions such as
(p)+q
, which looks, to a C or C++ programmer, as though it could be either a cast to typep
of a unary+
operating onq
, or a binary addition of two quantitiesp
andq
. In C and C++, the parser handles this problem by performing a limited amount of semantic analysis as it parses, so that it knows whetherp
is the name of a type or the name of a variable.
Java takes a different approach. The result of the
+
operator must be numeric, and all type names involved in casts on numeric values are known keywords. Thus, ifp
is a keyword naming a primitive type, then(p)+q
can make sense only as a cast of a unary expression. However, ifp
is not a keyword naming a primitive type, then(p)+q
can make sense only as a binary arithmetic operation. Similar remarks apply to the-
operator. The grammar shown above splits CastExpression into two cases to make this distinction. The nonterminal UnaryExpression includes all unary operators, but the nonterminal UnaryExpressionNotPlusMinus excludes uses of all unary operators that could also be binary operators, which in Java are+
and-
.
The second potential ambiguity is that the expression
(p)++
could, to a C or C++ programmer, appear to be either a postfix increment of a parenthesized expression or the beginning of a cast, for example, in(p)++q
. As before, parsers for C and C++ know whetherp
is the name of a type or the name of a variable. But a parser using only one-token lookahead and no semantic analysis during the parse would not be able to tell, when++
is the lookahead token, whether(p)
should be considered a Primary expression or left alone for later consideration as part of a CastExpression.
In Java, the result of the
++
operator must be numeric, and all type names involved in casts on numeric values are known keywords. Thus, ifp
is a keyword naming a primitive type, then(p)++
can make sense only as a cast of a prefix increment expression, and there had better be an operand such asq
following the++
. However, ifp
is not a keyword naming a primitive type, then(p)++
can make sense only as a postfix increment ofp
. Similar remarks apply to the--
operator. The nonterminal UnaryExpressionNotPlusMinus therefore also excludes uses of the prefix operators++
and--
.
15.25 Conditional Operator ? :
The conditional operator ? :
uses the boolean value of one expression to decide which of two other expressions should be evaluated.
- ConditionalExpression:
- ConditionalOrExpression
- ConditionalOrExpression
?
Expression:
ConditionalExpression - ConditionalOrExpression
?
Expression:
LambdaExpression #
The conditional operator is syntactically right-associative (it groups right-to-left). Thus, a?b:c?d:e?f:g
means the same as a?b:(c?d:(e?f:g))
.
The conditional operator has three operand expressions. ?
appears between the first and second expressions, and :
appears between the second and third expressions.
The first expression must be of type boolean
or Boolean
, or a compile-time error occurs.
It is a compile-time error for either the second or the third operand expression to be an invocation of a void
method.
In fact, by the grammar of expression statements (14.8), it is not permitted for a conditional expression to appear in any context where an invocation of a
void
method could appear.
There are three kinds of conditional expressions, classified according to the second and third operand expressions: boolean conditional expressions, numeric conditional expressions, and reference conditional expressions. The classification rules are as follows:
If both the second and the third operand expressions are boolean expressions, the conditional expression is a boolean conditional expression.
For the purpose of classifying a conditional, the following expressions are boolean expressions:
An expression of a standalone form (15.2) that has type
boolean
orBoolean
.A parenthesized
boolean
expression (15.8.5).A class instance creation expression (15.9) for class
Boolean
.A method invocation expression (15.12) for which the chosen most specific method (15.12.2.5) has return type
boolean
orBoolean
.Note that, for a generic method, this is the type before instantiating the method's type arguments.
A
boolean
conditional expression.A
switch
expression whose result expressions are allboolean
expressions.
If both the second and the third operand expressions are numeric expressions, the conditional expression is a numeric conditional expression.
For the purpose of classifying a conditional, the following expressions are numeric expressions:
An expression of a standalone form (15.2) with a type that is convertible to a numeric type (4.2, 5.1.8).
A parenthesized numeric expression (15.8.5).
A class instance creation expression (15.9) for a class that is convertible to a numeric type.
A method invocation expression (15.12) for which the chosen most specific method (15.12.2.5) has a return type that is convertible to a numeric type.
Note that, for a generic method, this is the type before instantiating the method's type arguments.
A numeric conditional expression.
A
switch
expression whose result expressions are all numeric expressions.
Otherwise, the conditional expression is a reference conditional expression.
The process for determining the type of a conditional expression depends on the kind of conditional expression, as outlined in the following sections.
...
15.25.1 Boolean Conditional Expressions
Boolean conditional expressions are standalone expressions (15.2).
The type of a boolean conditional expression is determined as follows:
If the second and third operands are both of type
Boolean
, the conditional expression has typeBoolean
.Otherwise, the conditional expression has type
boolean
.
15.25.2 Numeric Conditional Expressions
Numeric conditional expressions are standalone expressions (15.2).
The type of a numeric conditional expression is determined as follows:
If the second and third operands have the same type, then that is the type of the conditional expression.
If one of the second and third operands is of primitive type T, and the type of the other is the result of applying boxing conversion (5.1.7) to T, then the type of the conditional expression is T.
If one of the operands is of type
byte
orByte
and the other is of typeshort
orShort
, then the type of the conditional expression isshort
.If one of the operands is of type T where T is
byte
,short
, orchar
, and the other operand is a constant expression (15.2815.29) of typeint
whose value is representable in type T, then the type of the conditional expression is T.If one of the operands is of type T, where T is
Byte
,Short
, orCharacter
, and the other operand is a constant expression of typeint
whose value is representable in the type U which is the result of applying unboxing conversion to T, then the type of the conditional expression is U.Otherwise, binary numeric promotion (5.6) is applied to the second and third operand types, and the type of the conditional expression is the promoted type of the second and third operands.
Note that binary numeric promotion performs value set conversion (5.1.13) and may perform unboxing conversion (5.1.8).
15.25.3 Reference Conditional Expressions
A reference conditional expression is a poly expression if it appears in an assignment context or an invocation context (5.2. 5.3). Otherwise, it is a standalone expression.
Where a poly reference conditional expression appears in a context of a particular kind with target type T, its second and third operand expressions similarly appear in a context of the same kind with target type T.
A poly reference conditional expression is compatible with a target type T if its second and third operand expressions are compatible with T.
The type of a poly reference conditional expression is the same as its target type.
The type of a standalone reference conditional expression is determined as follows:
If the second and third operands have the same type (which may be the null type), then that is the type of the conditional expression.
If the type of one of the second and third operands is the null type, and the type of the other operand is a reference type, then the type of the conditional expression is that reference type.
Otherwise, the second and third operands are of types S1 and S2 respectively. Let T1 be the type that results from applying boxing conversion to S1, and let T2 be the type that results from applying boxing conversion to S2. The type of the conditional expression is the result of applying capture conversion (5.1.10) to lub(T1, T2).
Because reference conditional expressions can be poly expressions, they can "pass down" context to their operands. This allows lambda expressions and method reference expressions to appear as operands:
return ... ? (x `->` x) : (x `->` -x);
It also allows use of extra information to improve type checking of generic method invocations. Prior to Java SE 8, this assignment was well-typed:
List<String> ls = Arrays.asList();
but this was not:
List<String> ls = ... ? Arrays.asList() : Arrays.asList("a","b");
The rules above allow both assignments to be considered well-typed.
Note that a reference conditional expression does not have to contain a poly expression as an operand in order to be a poly expression. It is a poly expression simply by virtue of the context in which it appears. For example, in the following code, the conditional expression is a poly expression, and each operand is considered to be in an assignment context targeting
Class<? super Integer>
:
Class<? super Integer> choose(boolean b, Class<Integer> c1, Class<Number> c2) { return b ? c1 : c2; }
If the conditional expression was not a poly expression, then a compile-time error would occur, as its type would be lub(
Class<Integer>
,Class<Number>
) =Class<? extends Number>
which is incompatible with the return type ofchoose
.
15.28 switch
Expressions
A switch
expression is the expression analogue of the switch
statement (14.11). It consists of a selector expression and a switch block (14.11.1). A switch
expression matches the value of the selector expression against the switch labels associated with the switch block to determine which code contained in the switch block to execute to return a value. In contrast to a switch
statement, the switch block is checked to ensure that the switch
expression either completes normally with a value or completes abruptly.
- SwitchExpression:
switch
(
Expression)
SwitchBlock
The type of the selector expression must be char
, byte
, short
, int
, Character
, Byte
, Short
, Integer
, String
, or an enum type (8.9), or a compile-time error occurs.
15.28.1 The switch block of a switch
expression
Given a switch
expression, all of the following must be true or a compile-time error occurs:
The switch block must be compatible with the type of the selector expression (14.11.1).
No two of the
case
constants associated with the switch block may have the same value.Either there is exactly one
default
label associated with the switch block; or if the type of the selector expression is an enum type, then the set of all thecase
constants associated with the switch block must contain all the enum constants of that enum type.If the switch block consists of switch rules, then any switch rule block (14.11.1) cannot complete normally.
If the switch block consists of switch labeled statement groups, then the last statement in the switch block cannot complete normally (14.1), and the switch block must not have any switch labels after the last switch labeled statement group.
This forbids a
switch
expression to have an empty switch block.
Switch rules in
switch
expressions differ from those inswitch
statements (14.11.2). Inswitch
expressions there is no restriction on a switch rule expression, whereas inswitch
statements it must be a statement expression (14.8).
The result expressions of a switch
expression are determined as follows:
If the switch block consists of switch rules, then each is considered in turn:
if it introduces a switch rule expression, then this expression is a result expression of the
switch
expression.if it introduces a switch rule block, then every expression immediately contained in a
yield
statement in this block whose yield target is the givenswitch
expression is a result expression of theswitch
expression
If the switch block consists of switch labeled statement groups, then every expression immediately contained in a
yield
statement in the block whose yield target is the givenswitch
expression is a result expression of theswitch
expression.
It is a compile-time error if a switch
expression has no result expressions.
A switch
expression is a poly expression if it appears in an assignment context or an invocation context (5.2, 5.3). Otherwise, it is a standalone expression.
Where a poly switch
expression appears in a context of a particular kind with target type T, its result expressions similarly appear in a context of the same kind with target type T.
A poly switch
expression is compatible with a target type T if each of its result expressions is compatible with T.
The type of a poly switch
expression is the same as its target type.
The type of a standalone switch
expression is determined as follows:
If the result expressions all have the same type (which may be the
null
type), then that is the type of theswitch
expression.Otherwise, if the type of each result expression is
boolean
orBoolean
, an unboxing conversion (5.1.8) is applied to each result expression of typeBoolean
, and theswitch
expression has typeboolean
.Otherwise, if the type of each result expression is convertible to a numeric type (5.1.8), the type of the
switch
expression is the result of numeric promotion (5.6) applied to the result expressions.Otherwise, boxing conversion (5.1.7) is applied to each result expression that has a primitive type, after which the type of the
switch
expression is the result of applying capture conversion (5.1.10) to the least upper bound (4.10.4) of the types of the result expressions.
15.28.2 Run-Time Evaluation of switch
Expressions
When the switch
expression is executed, first the selector expression is evaluated; exactly one of three outcomes are possible.
If evaluation of the selector expression completes abruptly for some reason, the
switch
expression completes abruptly for the same reason.If the selector expression evaluates to
null
, then aNullPointerException
is thrown and the entireswitch
expression completes abruptly for that reason.Otherwise, execution continues by determining if a switch label associated with the switch block matches (14.11.1) the value of the selector expression.
If no switch label matches, then an
IncompatibleClassChangeError
is thrown and the entireswitch
expression completes abruptly for that reason.If a switch label matches, then one of the following applies:
If it is the switch label for a switch rule expression, then this expression is evaluated. If the result of evaluation is a value, then the
switch
expression completes normally with the same value.If it is the switch label for a switch rule block, then the block is executed.
If it is the switch label for a switch rule
throw
statement, then thethrow
statement is executed.Otherwise, all the statements in the switch block after the matching switch label are executed in order.
If execution of any statement or expression completes abruptly, it is handled as follows:
If execution of an expression completes abruptly for a reason, then the
switch
expression completes abruptly for the same reason.If execution of a statement completes abruptly for the reason of a
yield
with value V, then theswitch
expression completes normally and the value of theswitch
expression is V.If execution of a statement completes abruptly for any reason other than a
yield
with value, then theswitch
expression completes abruptly for the same reason.
15.28 15.29 Constant Expressions
- ConstantExpression:
- Expression
A constant expression is an expression denoting a value of primitive type or a String
that does not complete abruptly and is composed using only the following:
Literals of primitive type and literals of type
String
(3.10.1, 3.10.2, 3.10.3, 3.10.4, 3.10.5)Casts to primitive types and casts to type
String
(15.16)The unary operators
+
,-
,~
, and!
(but not++
or--
) (15.15.3, 15.15.4, 15.15.5, 15.15.6)The multiplicative operators
*
,/
, and%
(15.17)The additive operators
+
and-
(15.18)The shift operators
<<
,>>
, and>>>
(15.19)The relational operators
<
,<=
,>
, and>=
(but notinstanceof
) (15.20)The equality operators
==
and!=
(15.21)The bitwise and logical operators
&
,^
, and|
(15.22)The conditional-and operator
&&
and the conditional-or operator||
(15.23, 15.24)The ternary conditional operator
? :
(15.25)Parenthesized expressions (15.8.5) whose contained expression is a constant expression.
Simple names (6.5.6.1) that refer to constant variables (4.12.4).
Qualified names (6.5.6.2) of the form TypeName
.
Identifier that refer to constant variables (4.12.4).
Constant expressions of type String
are always "interned" so as to share unique instances, using the method String.intern
.
A constant expression is always treated as FP-strict (15.4), even if it occurs in a context where a non-constant expression would not be considered to be FP-strict.
Constant expressions are used as
case
labels inswitch
statements andswitch
expressions (14.11, 15.28) and have a special significance in assignment contexts (5.2) and the initialization of a class or interface (12.4.2). They may also govern the ability of awhile
,do
, orfor
statement to complete normally (14.21), and the type of a conditional operator? :
with numeric operands.
Example 15.28-1 15.29-1. Constant Expressions
true
(short)(1*2*3*4*5*6)
Integer.MAX_VALUE / 2
2.0 * Math.PI
"The integer " + Long.MAX_VALUE + " is mighty big."
Chapter 16: Definite Assignment
16.1 Definite Assignment and Expressions
16.1.7 switch
Expressions
Suppose that the switch expression has result expressions e1, ..., en, all of which are boolean-valued.
The following rules apply only if the switch block of a switch
expression (15.28) consists of switch labeled statement groups:
V is definitely assigned after a
switch
expression when true iff for everyyield
statement with an expression e in the switch block that may exit theswitch
expression, V is definitely assigned after e when true.V is definitely assigned after a
switch
expression when false iff for everyyield
statement with an expression e in the switch block that may exit theswitch
expression, V is definitely assigned after e when false.V is definitely unassigned after a
switch
expression when true iff for everyyield
statement with an expression e in the switch block that may exit theswitch
expression, V is definitely unassigned before theyield
statement and V is definitely unassigned after e when true.V is definitely unassigned after a
switch
expression when false iff for everyyield
statement with an expression e in the switch block that may exit theswitch
expression, V is definitely unassigned before theyield
statement and V is definitely unassigned after e when false.V is [un]assigned before the selector expression iff V is [un]assigned before the
switch
statement.V is [un]assigned before the first statement of the first switch labeled statement group in the switch block iff V is [un]assigned after the selector expression.
V is [un]assigned before the first statement of any switch labeled statement group other than the first iff V is [un]assigned after the selector expression and V is [un]assigned after the preceding statement.
The following rules apply only if the switch block of a switch
expression consists of switch rules:
V is definitely assigned after a
switch
expression when true iff for every switch rule one of the following is true:It introduces a switch rule expression e and V is definitely assigned after e when true.
It introduces a switch rule block b and for every
yield
statement with expression e contained in b that may exit theswitch
expression, V is definitely assigned after e when true.It introduces a switch rule
throw
statement.
V is definitely assigned after a
switch
expression when false iff for every switch rule one of the following is true:It introduces a switch rule expression e and V is definitely assigned after e when false.
It introduces a switch rule block b and for every
yield
statement with an expression e contained in b that may exit theswitch
expression, V is definitely assigned after e when false.It introduces a switch rule
throw
statement.
V is definitely unassigned after a
switch
expression when true iff for every switch rule one of the following is true:It introduces a switch rule expression e and V is definitely unassigned after e when true .
It introduces a switch rule block b and for every
yield
statement with expression e contained in b that may exit theswitch
expression, V is definitely unassigned before theyield
statement and V is definitely unassigned after e when true.It introduces a switch rule
throw
statement.
V is definitely unassigned after a
switch
expression when false iff for every switch rule one of the following is true:It introduces a switch rule expression e and V is definitely unassigned after e when false.
It introduces a switch rule block b and for every
yield
statement with expression e contained in b that may exit theswitch
expression, V is definitely unassigned before theyield
statement and V is definitely unassigned after e when false.It introduces a switch rule
throw
statement.
V is [un]assigned before any switch rule expression or switch rule statement in the switch block iff V is [un]assigned after the selector expression.
16.1.8 switch
Expressions
Suppose that the switch expression has result expressions e1
, ..., en
, not all of which are boolean-valued.
V is [un]assigned after a
switch
expression (15.28) iff all of the following are true:V is [un]assigned before every
yield
statement that may exit theswitch
statement.For each switch rule (14.11.1) in the switch block, V is [un]assigned after the switch rule expression, switch rule block, or switch rule
throw
statement introduced by the switch rule.
V is [un]assigned before the selector expression of a
switch
expression iff V is [un]assigned before theswitch
expression.V is [un]assigned before the switch rule expression, switch rule block, or switch rule
throw
statement introduced by a switch rule in the switch block iff V is [un]assigned after the selector expression of theswitch
expression.V is [un]assigned before the first block statement of a switch labeled statement group (14.11.1) of a
switch
expression iff both of following are true:V is [un]assigned after the selector expression of the
switch
statement.If the switch labeled statement group is not the first in the switch block, V is [un]assigned after the last block statement of the preceding switch labeled statement group.
V is [un]assigned before a block statement that is not the first of a switch labeled statement group of a
switch
expression iff V is [un]assigned after the preceding block statement.
16.1.7 16.1.9 Other Expressions of Type boolean
Suppose that e is an expression of type boolean
and is not a boolean constant expression, logical complement expression !
a, conditional-and expression a &&
b, conditional-or expression a ||
b, or conditional expression a ?
b :
c.
V is [un]assigned after e when true iff V is [un]assigned after e.
V is [un]assigned after e when false iff V is [un]assigned after e.
16.1.8 16.1.10 Assignment Expressions
Consider an assignment expression a =
b, a +=
b, a -=
b, a *=
b, a /=
b, a %=
b, a <<=
b, a >>=
b, a >>>=
b, a &=
b, a |=
b, or a ^=
b (15.26).
V is definitely assigned after the assignment expression iff either:
- a is V, or
- V is definitely assigned after b.
V is definitely unassigned after the assignment expression iff a is not V and V is definitely unassigned after b.
V is [un]assigned before a iff V is [un]assigned before the assignment expression.
V is [un]assigned before b iff V is [un]assigned after a.
Note that if a is V and V is not definitely assigned before a compound assignment such as a
&=
b, then a compile-time error will necessarily occur. The first rule for definite assignment stated above includes the disjunct "a is V" even for compound assignment expressions, not just simple assignments, so that V will be considered to have been definitely assigned at later points in the code. Including the disjunct "a is V" does not affect the binary decision as to whether a program is acceptable or will result in a compile-time error, but it affects how many different points in the code may be regarded as erroneous, and so in practice it can improve the quality of error reporting. A similar remark applies to the inclusion of the conjunct "a is not V" in the first rule for definite unassignment stated above.
16.1.9 16.1.11 Operators ++
and --
V is definitely assigned after
++
a (15.15.1),--
a (15.15.2), a++
(15.14.2), or a--
(15.14.3) iff either a is V or V is definitely assigned after the operand expression.V is definitely unassigned after
++
a,--
a, a++
, or a--
iff a is not V and V is definitely unassigned after the operand expression.V is [un]assigned before a iff V is [un]assigned before
++
a,--
a, a++
, or a--
.
16.1.10 16.1.12 Other Expressions
If an expression is not a boolean constant expression, and is not a preincrement expression ++
a, predecrement expression --
a, postincrement expression a++
, postdecrement expression a--
, logical complement expression !
a, conditional-and expression a &&
b, conditional-or expression a ||
b, conditional expression a ?
b :
c, assignment expression, or lambda expression, then the following rules apply:
If the expression has no subexpressions, V is [un]assigned after the expression iff V is [un]assigned before the expression.
This case applies to literals, names,
this
(both qualified and unqualified), unqualified class instance creation expressions with no arguments, array creation expressions with initializers that contain no expressions, superclass field access expressions, unqualified and type-qualified method invocation expressions with no arguments, superclass method invocation expressions with no arguments, and superclass and type-qualified method reference expressions.If the expression has subexpressions, V is [un]assigned after the expression iff V is [un]assigned after its rightmost immediate subexpression.
There is a piece of subtle reasoning behind the assertion that a variable V can be known to be definitely unassigned after a method invocation expression. Taken by itself, at face value and without qualification, such an assertion is not always true, because an invoked method can perform assignments. But it must be remembered that, for the purposes of the Java programming language, the concept of definite unassignment is applied only to blank
final
variables. If V is a blankfinal
local variable, then only the method to which its declaration belongs can perform assignments to V. If V is a blankfinal
field, then only a constructor or an initializer for the class containing the declaration for V can perform assignments to V; no method can perform assignments to V. Finally, explicit constructor invocations (8.8.7.1) are handled specially (16.9); although they are syntactically similar to expression statements containing method invocations, they are not expression statements and therefore the rules of this section do not apply to explicit constructor invocations.
If an expression is a lambda expression, then the following rules apply:
V is [un]assigned after the expression iff V is [un]assigned before the expression.
V is definitely assigned before the expression or block that is the lambda body (15.27.2) iff V is definitely assigned before the lambda expression.
No rule allows V to be definitely unassigned before a lambda body. This is by design: a variable that was definitely unassigned before the lambda body may end up being assigned to later on, so we cannot conclude that the variable will be unassigned when the body is executed.
For any immediate subexpression y of an expression x, where x is not a lambda expression, V is [un]assigned before y iff one of the following is true:
y is the leftmost immediate subexpression of x and V is [un]assigned before x.
y is the right-hand operand of a binary operator and V is [un]assigned after the left-hand operand.
x is an array access, y is the subexpression within the brackets, and V is [un]assigned after the subexpression before the brackets.
x is a primary method invocation expression, y is the first argument expression in the method invocation expression, and V is [un]assigned after the primary expression that computes the target object.
x is a method invocation expression or a class instance creation expression; y is an argument expression, but not the first; and V is [un]assigned after the argument expression to the left of y.
x is a qualified class instance creation expression, y is the first argument expression in the class instance creation expression, and V is [un]assigned after the primary expression that computes the qualifying object.
x is an array creation expression; y is a dimension expression, but not the first; and V is [un]assigned after the dimension expression to the left of y.
x is an array creation expression initialized via an array initializer; y is the array initializer in x; and V is [un]assigned after the dimension expression to the left of y.
16.2 Definite Assignment and Statements
16.2.9 switch
Statements
V is [un]assigned after a
switch
statement (14.11) iff all of the following are true:Either there is a
default
label in the switch block or V is [un]assigned after the switch expression.Either there are no switch labels in the switch block that do not begin a block-statement-group (that is, there are no switch labels immediately before the "
}
" that ends the switch block) or V is [un]assigned after the switch expression.Either the switch block contains no block-statement-groups or V is [un]assigned after the last block-statement of the last block-statement-group.
V is [un]assigned before every
break
statement that may exit theswitch
statement.
V is [un]assigned before the switch expression iff V is [un]assigned before the
switch
statement.
If a switch block contains at least one block-statement-group, then the following rules also apply:
V is [un]assigned before the first block-statement of the first block-statement-group in the switch block iff V is [un]assigned after the switch expression.
V is [un]assigned before the first block-statement of any block-statement-group other than the first iff V is [un]assigned after the switch expression and V is [un]assigned after the preceding block-statement.
V is [un]assigned after a
switch
statement (14.11) iff all of the following are true:V is [un]assigned before every
break
statement that may exit theswitch
statement.For each switch rule (14.11.1) in the switch block, V is [un]assigned after the switch rule expression, switch rule block, or switch rule
throw
statement introduced by the switch rule.If there is a switch labeled statement group (14.11.1) in the switch block, then V is [un]assigned after the last block statement of the last switch labeled statement group.
If there is no
default
label in the switch block, or if the switch block ends with a switch label followed by}
, then V is [un]assigned after the selector expression.
V is [un]assigned before the selector expression of a
switch
statement iff V is [un]assigned before theswitch
statement.V is [un]assigned before the switch rule expression, switch rule block, or switch rule
throw
statement introduced by a switch rule in the switch block iff V is [un]assigned after the selector expression of theswitch
statement.V is [un]assigned before the first block statement of a switch labeled statement group of the switch block iff both of the following are true:
V is [un]assigned after the selector expression of the
switch
statement.If the switch labeled statement group is not the first in the switch block, V is [un]assigned after the last block statement of the preceding switch labeled statement group.
V is [un]assigned before a block statement that is not the first of a switch labeled statement group of a
switch
statement iff V is [un]assigned after the preceding block statement.
16.2.13 break
, yield
, continue
, return
, and throw
Statements
By convention, we say that V is [un]assigned after any
break
,yield
,continue
,return
, orthrow
statement (14.15, 14.21, 14.16, 14.17, 14.18).The notion that a variable is "[un]assigned after" a statement or expression really means "is [un]assigned after the statement or expression completes normally". Because a
break
,continue
,return
, orthrow
statement never completes normally, it vacuously satisfies this notion.In a
return
statement with an expression e,or athrow
statement with an expression e, or ayield
statement with an expression e, V is [un]assigned before e iff V is [un]assigned before thereturn
,orthrow
oryield
statement.
Chapter 18: Type Inference
18.2 Reduction
18.2.1 Expression Compatibility Constraints
A constraint formula of the form ‹Expression → T› is reduced as follows:
If T is a proper type, the constraint reduces to true if the expression is compatible in a loose invocation context with T (5.3), and false otherwise.
Otherwise, if the expression is a standalone expression (15.2) of type S, the constraint reduces to ‹S → T›.
Otherwise, the expression is a poly expression (15.2). The result depends on the form of the expression:
If the expression is a parenthesized expression of the form
(
Expression')
, the constraint reduces to ‹Expression' → T›.If the expression is a class instance creation expression or a method invocation expression, the constraint reduces to the bound set B3 which would be used to determine the expression's compatibility with target type T, as defined in 18.5.2.1. (For a class instance creation expression, the corresponding "method" used for inference is defined in 15.9.3.)
This bound set may contain new inference variables, as well as dependencies between these new variables and the inference variables in T.
If the expression is a conditional expression of the form e1
?
e2:
e3, the constraint reduces to two constraint formulas, ‹e2 → T› and ‹e3 → T›.If the expression is a
switch
expression with a set of result expressions e1, ..., en, the constraint reduces to n constraint formulas, ‹e1 → T›, ..., ‹en → T›.If the expression is a lambda expression or a method reference expression, the result is specified below.
By treating nested generic method invocations as poly expressions, we improve the behavior of inference for nested invocations. For example, the following is illegal in Java SE 7 but legal in Java SE 8:
ProcessBuilder b = new ProcessBuilder(Collections.emptyList()); // ProcessBuilder's constructor expects a List<String>
When both the outer and the nested invocation require inference, the problem is more difficult. For example:
List<String> ls = new ArrayList<>(Collections.emptyList());
Our approach is to "lift" the bounds inferred for the nested invocation (simply { α
<:
Object
} in the case ofemptyList
) into the outer inference process (in this case, trying to infer β where the constructor is for typeArrayList<
β>
). We also infer dependencies between the nested inference variables and the outer inference variables (the constraint ‹List<
α>
→Collection<
β>
› would reduce to the dependency α = β). In this way, resolution of the inference variables in the nested invocation can wait until additional information can be inferred from the outer invocation (based on the assignment target, β =String
).
...
18.5 Uses of Inference
18.5.2 Invocation Type Inference
18.5.2.2 Additional Argument Constraints
The invocation type for the chosen method is determined after considering additional constraints that may be implied by the argument expressions of the method invocation expression, as follows:
If the method invocation expression is a poly expression, let B3 be the bound set generated in 18.5.2.1 to demonstrate compatibility with the actual target type of the method invocation.
If the method invocation expression is not a poly expression, let B3 be the same as the bound set produced by reduction in order to demonstrate that m is applicable in 18.5.1.
(While it was necessary in 18.5.1 and 18.5.2.1 to demonstrate that the inference variables in the bound set could be resolved, the instantiations produced by these resolution steps are not considered part of B3.)
A set of constraint formulas, C, is generated as follows.
Let e1, ..., ek be the actual argument expressions of the method invocation expression.
If m is applicable by strict or loose invocation, let F1, ..., Fk be the formal parameter types of m; if m is applicable by variable arity invocation, let F1, ..., Fk the first k variable arity parameter types of m (15.12.2.4).
Let θ be the substitution
[
P1:=α1, ..., Pp:=αp]
defined in 18.5.1 to replace the type parameters of m with inference variables.Then, for all i (1 ≤ i ≤ k):
If ei is not pertinent to applicability, C contains ‹ei → Fi θ›.
Additional constraints may be included, depending on the form of ei:
If ei is a LambdaExpression, C contains ‹LambdaExpression →throws Fi θ›, and the lambda body is searched for additional constraints:
For a block lambda body, the search is applied recursively to each result expression.
For a poly class instance creation expression or a poly method invocation expression , C contains all the constraint formulas that would appear in the set C generated by 18.5.2 when inferring the poly expression's invocation type.
For a parenthesized expression, the search is applied recursively to the contained expression.
For a conditional expression, the search is applied recursively to the second and third operands.
For a
switch
expression, the search is applied recursively to each of its result expressions.For a lambda expression, the search is applied recursively to the lambda body.
If ei is a MethodReference, C contains ‹MethodReference →throws Fi θ›.
If ei is a poly class instance creation expression or a poly method invocation expression, C contains all the constraint formulas that would appear in the set C generated by 18.5.2 when inferring the poly expression's invocation type.
If ei is a parenthesized expression, these rules are applied recursively to the contained expression.
If ei is a conditional expression, these rules are applied recursively to the second and third operands.
If ei is a
switch
expression, these rules are applied recursively to each of its result expressions.
While C is not empty, the following process is repeated, starting with the bound set B3 and accumulating new bounds into a "current" bound set, ultimately producing a new bound set, B4:
A subset of constraints is selected in C, satisfying the property that, for each constraint, no input variable can influence an output variable of another constraint in C. The terms input variable and output variable are defined below. An inference variable α can influence an inference variable β if α depends on the resolution of β (18.4), or vice versa; or if there exists a third inference variable γ such that α can influence γ and γ can influence β.
If this subset is empty, then there is a cycle (or cycles) in the graph of dependencies between constraints. In this case, the constraints in C that participate in a dependency cycle (or cycles) and do not depend on any constraints outside of the cycle (or cycles) are considered. A single constraint is selected from these considered constraints, as follows:
If any of the considered constraints have the form ‹Expression → T›, then the selected constraint is the considered constraint of this form that contains the expression to the left (3.5) of the expression of every other considered constraint of this form.
If no considered constraint has the form ‹Expression → T›, then the selected constraint is the considered constraint that contains the expression to the left of the expression of every other considered constraint.
The selected constraint(s) are removed from C.
The input variables α1, ..., αm of all the selected constraint(s) are resolved.
Where T1, ..., Tm are the instantiations of α1, ..., αm, the substitution
[
α1:=T1, ..., αm:=Tm]
is applied to every constraint.The constraint(s) resulting from substitution are reduced and incorporated with the current bound set.
Finally, if B4 does not contain the bound false, the inference variables in B4 are resolved.
If resolution succeeds with instantiations T1, ..., Tp for inference variables α1, ..., αp, let θ' be the substitution
[
P1:=T1, ..., Pp:=Tp]
. Then:If unchecked conversion was necessary for the method to be applicable during constraint set reduction in 18.5.1, then the parameter types of the invocation type of m are obtained by applying θ' to the parameter types of m's type, and the return type and thrown types of the invocation type of m are given by the erasure of the return type and thrown types of m's type.
If unchecked conversion was not necessary for the method to be applicable, then the invocation type of m is obtained by applying θ' to the type of m.
If B4 contains the bound false, or if resolution fails, then a compile-time error occurs.
The process of reducing additional argument constraints may require carefully ordering constraint formulas of the forms ‹Expression → T›, ‹LambdaExpression →throws T›, and ‹MethodReference →throws T›. To facilitate this ordering, the input variables of these constraints are defined as follows:
For ‹LambdaExpression → T›:
If T is an inference variable, it is the (only) input variable.
If T is a functional interface type, and a function type can be derived from T (15.27.3), then the input variables include (i) if the lambda expression is implicitly typed, the inference variables mentioned by the function type's parameter types; and (ii) if the function type's return type, R, is not
void
, then for each result expression e in the lambda body (or for the body itself if it is an expression), the input variables of ‹e → R›.Otherwise, there are no input variables.
For ‹LambdaExpression →throws T›:
If T is an inference variable, it is the (only) input variable.
If T is a functional interface type, and a function type can be derived, as described in 15.27.3, the input variables include (i) if the lambda expression is implicitly typed, the inference variables mentioned by the function type's parameter types; and (ii) the inference variables mentioned by the function type's return type.
Otherwise, there are no input variables.
For ‹MethodReference → T›:
If T is an inference variable, it is the (only) input variable.
If T is a functional interface type with a function type, and if the method reference is inexact (15.13.1), the input variables are the inference variables mentioned by the function type's parameter types.
Otherwise, there are no input variables.
For ‹MethodReference →throws T›:
If T is an inference variable, it is the (only) input variable.
If T is a functional interface type with a function type, and if the method reference is inexact (15.13.1), the input variables are the inference variables mentioned by the function type's parameter types and the function type's return type.
Otherwise, there are no input variables.
For ‹Expression → T›, if Expression is a parenthesized expression:
Where the contained expression of Expression is Expression', the input variables are the input variables of ‹Expression' → T›.
For ‹ConditionalExpression → T›:
Where the conditional expression has the form e1
?
e2:
e3, the input variables are the input variables of ‹e2 → T› and ‹e3 → T›.For ‹SwitchExpression → T›:
Where the switch expression has result expression e1, ..., en, the input variables are, for all i, 1 ≤ i ≤ n, the input variables of ‹ei → T›.
For all other constraint formulas, there are no input variables.
The output variables of these constraints are all inference variables mentioned by the type on the right-hand side of the constraint, T, that are not input variables.
18.5.4 More Specific Method Inference
When testing that one applicable method is more specific than another (15.12.2.5), where the second method is generic, it is necessary to test whether some instantiation of the second method's type parameters can be inferred to make the first method more specific than the second.
Let m1 be the first method and m2 be the second method. Where m2 has type parameters P1, ..., Pp, let α1, ..., αp be inference variables, and let θ be the substitution [
P1:=α1, ..., Pp:=αp]
.
Let e1, ..., ek be the argument expressions of the corresponding invocation. Then:
If m1 and m2 are applicable by strict or loose invocation (15.12.2.2, 15.12.2.3), then let S1, ..., Sk be the formal parameter types of m1, and let T1, ..., Tk be the result of θ applied to the formal parameter types of m2.
If m1 and m2 are applicable by variable arity invocation (15.12.2.4), then let S1, ..., Sk be the first k variable arity parameter types of m1, and let T1, ..., Tk be the result of θ applied to the first k variable arity parameter types of m2.
Note that no substitution is applied to S1, ..., Sk; even if m1 is generic, the type parameters of m1 are treated as type variables, not inference variables.
The process to determine if m1 is more specific than m2 is as follows:
First, an initial bound set, B, is generated from the declared bounds of P1, ..., Pp, as specified in 18.1.3.
Second, for all i (1 ≤ i ≤ k), a set of constraint formulas or bounds is generated.
If Ti is a proper type, the result is true if Si is more specific than Ti for ei (15.12.2.5), and false otherwise. (Note that Si is always a proper type.)
Otherwise, if Si and Ti are not both functional interface types, the constraint formula ‹Si
<:
Ti› is generated.Otherwise, if the interface of Si is a superinterface or a subinterface of the interface of Ti (or, where Si or Ti is an intersection type, some interface of Si is a superinterface or a subinterface of some interface of Ti), the constraint formula ‹Si
<:
Ti› is generated.Otherwise, let MTS be the function type of the capture of Si, let MTS' be the function type of Si (without capture), and let MTT be the function type of Ti. If MTS and MTT have a different number of formal parameters or type parameters, or if MTS and MTS' do not have the same type parameters (8.4.4), the result is false. Otherwise, the following constraint formulas or bounds are generated from the type parameters, formal parameter types, and return types of MTS and MTT:
Let A1, ..., An be the type parameters of MTS, and let B1, ..., Bn be the type parameters of MTT.
Let θ' be the substitution
[
B1:=A1, ..., Bn:=An]
. Then, for all j (1 ≤ j ≤ n):If the bound of Aj mentions one of A1, ..., An, and the bound of Bj is a not proper type, false.
Otherwise, where X is the bound of Aj and Y is the bound of Bj, ‹X = Y θ'›.
If the bound Aj mentions one of A1, ..., An, and the bound of Bj is not a proper type, then producing an equality constraint would raise the possibility of an inference variable being bounded by an out-of-scope type variable. Since instantiating an inference variable with an out-of-scope type variable is nonsensical, we prefer to avoid the situation by giving up immediately whenever the possibility arises. This simplification is not completeness-preserving. (The same comment applies to the treatment of formal parameter types and return types below.)
Let U1, ..., Uk be the formal parameter types of MTS, and let V1, ..., Vk be the formal parameter types of MTT. Then, for all j (1 ≤ j ≤ k):
If Uj mentions one of A1, ..., An, and Vj is not a proper type, false.
Otherwise, ‹Vj θ'
<:
Uj›, and, where U1', ..., Uk' are the formal parameter types of MTS', and A1', ..., An' are the type parameters of MTS', ‹Vj[
B1:=A1', ..., Bn:=An']
= Uj'›
Let RS be the return type of MTS, and let RT be the return type of MTT. Then:
If RS mentions one of A1, ..., An, and RT is not a proper type, false.
Otherwise, if ei is an explicitly typed lambda expression:
If RT is
void
, true.Otherwise, if RS and RT are functional interface types, and ei has at least one result expression, then for each result expression in ei, this entire second step is repeated to infer constraints under which RS is more specific than RT θ' for the given result expression.
Otherwise, if RS is a primitive type and RT is not, and ei has at least one result expression, and each result expression of ei is a standalone expression (15.2) of a primitive type, true.
Otherwise, if RT is a primitive type and RS is not, and ei has at least one result expression, and each result expression of ei is either a standalone expression of a reference type or a poly expression, true.
Otherwise, ‹RS
<:
RT θ'›.
Otherwise, if ei is an exact method reference:
If RT is
void
, true.Otherwise, if RS is a primitive type and RT is not, and the compile-time declaration for ei has a primitive return type, true.
Otherwise if RT is a primitive type and RS is not, and the compile-time declaration for ei has a reference return type, true.
Otherwise, ‹RS
<:
RT θ'›.
Otherwise, if ei is a parenthesized expression, these rules for constraints derived from RS and RT are applied recursively for the contained expression.
Otherwise, if ei is a conditional expression, these rules for constraints derived from RS and RT are applied recursively for each of the second and third operands.
Otherwise, if ei is a
switch
expression, these rules for constraints derived from RS and RT are applied recursively for each of the result expressions.Otherwise, false.
Third, if m2 is applicable by variable arity invocation and has k+1 parameters, then where Sk+1 is the k+1'th variable arity parameter type of m1 and Tk+1 is the result of θ applied to the k+1'th variable arity parameter type of m2, the constraint ‹Sk+1
<:
Tk+1› is generated.Fourth, the generated bounds and constraint formulas are reduced and incorporated with B to produce a bound set B'.
If B' does not contain the bound false, and resolution of all the inference variables in B' succeeds, then m1 is more specific than m2.
Otherwise, m1 is not more specific than m2.