Changes to the Java(R) Language Specification
Copyright (C) 2019 Oracle America, Inc.
Last updated 2019-05-28
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 new yield statement, allows a switch to produce a value.
This version of the draft specification uses a new yield statement to produce a value for a switch expression. The first draft of this specification used a break-with statement.
See JEP 354 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.
An identifier is an unlimited-length sequence of Java letters and Java digits, the first of which must be a Java letter.
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:
Stringi3- αρετη
MAX_VALUEisLetterOrDigit
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.
var or yield
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
varoryield(8.1).
An unqualified method identifier is an identifier that is not the character sequence yield.
yield
This restriction allows
yieldto be used in ayieldstatement (14.21) and still also be used as a (qualified) method name for compatibility reasons.
51 character sequences, formed from ASCII letters, are reserved for use as keywords and cannot be used as identifiers (3.8).
abstract continue for new switchassert default if package synchronizedboolean do goto private thisbreak double implements protected throwbyte else import public throwscase enum instanceof return transientcatch extends int short trychar final interface static voidclass finally long strictfp volatileconst float native super while_ (underscore)
The keywords
constandgotoare 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:
trueandfalseare not keywords, but rather boolean literals (3.10.3).
nullis not a keyword, but rather the null literal (3.10.7).
varis 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
varandyieldare not keywords.varhas 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).yieldhas special meaning in ayieldstatement (14.21). All invocations of a method calledyieldmust be qualified so as to be distinguished from ayieldstatement.
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.
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 type Thread requires a run-time check to make sure that the run-time value is actually an instance of class Thread or one of its subclasses; if it is not, an exception is thrown.
A conversion from type Thread to type Object requires no run-time action; Thread is a subclass of Object, so any reference produced by an expression of type Thread is a valid reference value of type Object.
A conversion from type int to type long requires run-time sign-extension of a 32-bit integer value to the 64-bit long representation. No information is lost.
A conversion from type double to type long 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:
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
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. A long shift distance (right operand) does not promote the value being shifted (left operand) to long.
an operand of a binary operator +, ==, !=, &, ^, or |, where the operands are both convertible to a numeric type
the 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 is not an 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 is double, and other expressions that are not of type double undergo widening primitive conversion to double.
Otherwise, if any expression is of type float, then the promoted type is float and other expressions that are not of type float undergo widening primitive conversion to float.
Otherwise, if any expression is of type long, then the promoted type is long and other expressions that are not of type long undergo widening primitive conversion to long.
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 type int undergo widening primitive conversion to int.
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 is int and other expressions that are not of type int undergo widening primitive conversion to int.
Otherwise, if any expression is of type short, and every other expression is either of type short, or of type byte, or a constant expression of type int with a value that is representable in the type short, then T is short, the byte expressions undergo widening primitive conversion to short, and the int expressions undergo narrowing primitive conversion to short.
Otherwise, if any expression is of type byte, and every other expression is either of type byte or a constant expression of type int with a value that is representable in the type byte, then T is byte and the int expressions undergo narrowing primitive conversion to byte.
Otherwise, if any expression is of type char, and every other expression is either of type char or a constant expression of type int with a value that is representable in the type char, then T is char and the int expressions undergo narrowing primitive conversion to char.
Otherwise, T is int and all the expressions that are not of type int undergo widening primitive conversion to int.
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.
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 is are less expressive than the other six five categories, because it is they are denoted with TypeIdentifier and UnqualifiedMethodIdentifier, respectively. In the former case this , which excludes the character sequence sequences var and yield (3.8), and in the later it excludes the character sequence yield.
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).
. Identifier
. Identifier
. TypeIdentifier
. Identifier
. Identifier
. 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.
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
varandyieldas a type name. It is usually redundant, because the rules for declarations already prevent the introduction of types namedvarandyield. However, in some cases, a compiler may find a binary class namedvaroryield, 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 package org (and we know that there is no such class or interface because package org has a subpackage named rpgpoet), the qualified name org.rpgpoet is reclassified as a PackageName.
Next, because package org.rpgpoet has an accessible (6.6) interface type named Music, the qualified name org.rpgpoet.Music is reclassified as a TypeName.
Finally, because the name org.rpgpoet.Music is a TypeName, the qualified name org.rpgpoet.Music.wizards is reclassified as an ExpressionName.
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; 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 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 labeled expression, switch labeled block, switch labeled throw statement, or 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.
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.FileNotFoundExceptiononly. 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 the try statement which declares C can throw; and
E 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 same try 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 a try-with-resources statement) can throw E, or the automatic invocation of the close() method of a resource (in a try-with-resources statement) can throw E, and E is not assignment compatible with any catchable exception class of any catch clause of the try statement, and either no finally block is present or the finally block can complete normally; or
Some catch block of the try statement can throw E and either no finally block is present or the finally block can complete normally; or
A 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 labeled expression, switch labeled block, switch labeled throw statement, or 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.
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), yield (14.21), continue (14.16), and return (14.17) 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 label
A break with a given label
A yield with a given value
A continue with no label
A continue with a given label
A return with no value
A return with a given value
A throw with a given value, including exceptions thrown by the Java Virtual Machine
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 (Chapter 11 (Exceptions), 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.
:::
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:
The following productions from [14.9] are shown here for convenience:
- IfThenStatement:
if(Expression)Statement- IfThenElseStatement:
if(Expression)StatementNoShortIfelseStatement- IfThenElseStatementNoShortIf:
if(Expression)StatementNoShortIfelseStatementNoShortIf
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.
switch StatementMuch 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.
switch ( Expression ) SwitchBlock
{ {SwitchBlockStatementGroup} {SwitchLabel} }
case ConstantExpression :
case EnumConstantName :
default :
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.
{ SwitchLabeledRule {SwitchLabeledRule} }
{ {SwitchLabeledStatementGroup} {SwitchLabel :} }
-> Expression ;
-> Block
-> ThrowStatement
case CaseConstant {, CaseConstant}
default
: {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 labeled rules, which use -> to introduce either a switch labeled expression, switch labeled block, or a switch labeled throw statement; or
Switch 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 labeled rule has a switch label and either an expression, block, or 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
nullas acaseconstant.
Given a switch statement, all of the following must be true or a compile-time error occurs:
Every case constant associated with the switch statement must be assignment compatible with the type of the switch statement's Expression (5.2).
If the type of the switch statement's Expression is an enum type, then every case constant associated with the switch statement must be an enum constant of that type.
No two of the case constants associated with the switch statement have the same value.
No case constant associated with the switch statement is null.
At most one default label is associated with the switch 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 the case label matches. Equality is defined in terms of the == operator (15.21) unless the value of the selector expression is a String, in which case equality is defined in terms of the equals method of class String.
If no case label matches but there is a default label, then we say that the default label matches.
A
caselabel can contain severalcaseconstants, and matches the value of a selector expression if any one of itscaseconstants matches.
The prohibition against using
nullas acaseconstant prevents code being written that can never be executed. If theselector expression is of a reference type, that is,switchstatement's ExpressionStringor a boxed primitive type or an enum type, then an exceptionwill be thrownwill occur if the Expression evaluates tonullat 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 theswitchstatement or choosing to execute the statements (if any) after thedefaultlabel (if any)defaultlabel matching.
A Java compiler is encouraged (but not required) to provide a warning if aswitchon an enum-valued expression lacks adefaultlabel and lackscaselabels for one or more of the enum's constants. Such aswitchwill silently do nothing if the expression evaluates to one of the missing constants.
In C and C++ the body of a
switchstatement can be a statement and statements withcaselabelsheads do not have to be immediately contained by that statement. Consider the simple loop:for (i = 0; i < n; ++i) foo();where
nis 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.
switch Block of a switch StatementGiven 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 labeled expressions in the switch block must be statement expressions (14.8).
Switch labeled rules in
switchstatements differ from those inswitchexpressions (15.28). Inswitchstatements they must be switch labeled statement expressions, whereas inswitchexpressions they may be any switch labeled expression (15.28.1).
switch statementA 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 the case label matches. Equality is defined in terms of the == operator (15.21) unless the value of the expression is a String, in which case equality is defined in terms of the equals method of class String.
All statements after the matching case label in the switch 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 entire switch statement completes normally.
If no case label matches but there is a default label, then all statements after the default label in the switch 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 entire switch statement completes normally.
If no case label matches and there is no default label, then no further action is taken and the switch 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 a NullPointerException is thrown and the entire switch 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 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 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 throw statement, then the throw 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 Statement a statement completes abruptly because of a break with no label, no further action is taken and the switch statement completes normally.
If execution of the Statement a statement or expression completes abruptly for any other reason, the switch statement completes abruptly for the same reason.
The case of abrupt completion because of a
yieldstatement is handled by the general rule forswitchexpressions (15.28).
The case of abrupt completion because of a
breakwith 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 labeled rule, the labeled expression or statement 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 labeled 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
break StatementA break statement transfers control out of an enclosing statement.
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
breakstatement always completes abruptly.
The preceding descriptions say "attempts to transfer control" rather than just "transfers control" because if there are any
trystatements (14.20) within the break target whosetryblocks orcatchclauses contain thebreakstatement, then anyfinallyclauses of thosetrystatements are executed, in order, innermost to outermost, before control is transferred to the break target. Abrupt completion of afinallyclause can disrupt the transfer of control initiated by abreakstatement.
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.
continue StatementA 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.
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
continuestatement always completes abruptly.
See the descriptions of the
whilestatement (14.12),dostatement (14.13), andforstatement (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
trystatements (14.20) within the continue target whosetryblocks orcatchclauses contain thecontinuestatement, then anyfinallyclauses of thosetrystatements are executed, in order, innermost to outermost, before control is transferred to the continue target. Abrupt completion of afinallyclause can disrupt the transfer of control initiated by acontinuestatement.
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.
return StatementA return statement returns control to the invoker of a method (8.4, 15.12) or constructor (8.8, 15.9).
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 keyword void, 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 value
A 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
returnstatement always completes abruptly.
The preceding descriptions say "attempts to transfer control" rather than just "transfers control" because if there are any
trystatements (14.20) within the method or constructor whosetryblocks orcatchclauses contain thereturnstatement, then anyfinallyclauses of thosetrystatements will be executed, in order, innermost to outermost, before control is transferred to the invoker of the method or constructor. Abrupt completion of afinallyclause can disrupt the transfer of control initiated by areturnstatement.
yield StatementThis 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.
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
breakstatement, ayieldstatement 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.
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, andforstatements 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
nis known at compile time and in principle it can be known at compile time that the assignment tokcan 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 initializer static initializer, lambda expression, or switch 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 the if-then statement is reachable.
An if-then-else statement can complete normally iff the then-statement can complete normally or the else-statement can complete normally.
The then-statement is reachable iff the if-then-else statement is reachable.
The else-statement is reachable iff the if-then-else statement is reachable.
This handling of an
ifstatement, whether or not it has anelsepart, 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 the switch statement.
A switch statement with a switch block that consists of switch labeled rules can complete normally iff at least one of the following is true:
One of the switch labeled rules is a switch labeled expression (which is necessarily a statement expression).
One of the switch labeled rules is a switch labeled block that can complete normally.
One of the switch labeled rules is a switch labeled block that contains a reachable break statement that exits the switch 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 its the switch block is reachable and at least one of the following is true:switch statement
It bears a case or default label.
There is a statement preceding it in the switch block and that preceding statement can complete normally.
A switch labeled block 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.28 15.29) with value true.
There is a reachable break statement that exits the while statement.
The contained statement is reachable iff the while statement is reachable and the condition expression is not a constant expression whose value is false.
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.28 15.29) with value true.
The do statement contains a reachable continue statement with no label, and the do statement is the innermost while, do, or for statement that contains that continue statement, and the continue statement continues that do statement, and the condition expression is not a constant expression with value true.
The do statement contains a reachable continue statement with a label L, and the do statement has label L, and the continue statement continues that do statement, and the condition expression is not a constant expression with value true.
There is a reachable break statement that exits the do 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.28 15.29) with value true.
There is a reachable break statement that exits the for statement.
The contained statement is reachable iff the for statement is reachable and the condition expression is not a constant expression whose value is false.
An enhanced for statement can complete normally iff it is reachable.
A break, yield, continue, return, or throw 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 any catch block can complete normally.
If the try statement has a finally block, then the finally block can complete normally.
The try block is reachable iff the try 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 of Exception, or some expression or throw statement in the try 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 the try 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 the catch block is reachable.
If a finally block is present, it is reachable iff the try statement is reachable.
One might expect the
ifstatement to be handled in the following manner:
An
if-thenstatement can complete normally iff at least one of the following is true:
The
if-thenstatement 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-thenstatement is reachable and the condition expression is not a constant expression whose value isfalse.An
if-then-elsestatement can complete normally iff thethen-statement can complete normally or theelse-statement can complete normally.The
then-statement is reachable iff theif-then-elsestatement is reachable and the condition expression is not a constant expression whose value isfalse.The
else-statement is reachable iff theif-then-elsestatement 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
ifstatement 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 generatedclassfile, 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
DEBUGfromfalsetotrueor fromtruetofalseand 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
staticconstant 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 (noLinkageErroroccurs) but not behaviorally compatible.
Another reason for "inlining" values of
staticconstant variables is because ofswitchstatements. They are the only kind of statement that relies on constant expressions, namely that eachcaselabel of aswitchstatement must be a constant expression whose value is different than every othercaselabel.caselabels are often references tostaticconstant 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 theclassfile 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:
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:
ensuring that this value is not a constant expression. Similar idioms exist for the other primitive types.
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.
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:
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).
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 is null.
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 the length of the array.
A field access expression (15.11) throws a NullPointerException if the value of the object reference expression is null.
A method invocation expression (15.12) that invokes an instance method throws a NullPointerException if the target reference is null.
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.
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) not static 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.
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 be abstract.
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.
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
- UnaryExpression
++ UnaryExpression
-- UnaryExpression
~ UnaryExpression
! UnaryExpression
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 typepof a unary+operating onq, or a binary addition of two quantitiespandq. In C and C++, the parser handles this problem by performing a limited amount of semantic analysis as it parses, so that it knows whetherpis 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, ifpis a keyword naming a primitive type, then(p)+qcan make sense only as a cast of a unary expression. However, ifpis not a keyword naming a primitive type, then(p)+qcan 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 whetherpis 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, ifpis 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 asqfollowing the++. However, ifpis 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--.
? :The conditional operator ? : uses the boolean value of one expression to decide which of two other expressions should be evaluated.
? Expression : ConditionalExpression
? 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
voidmethod 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 or Boolean.
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 or Boolean.
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 all boolean 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.
...
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 type Boolean.
Otherwise, the conditional expression has type boolean.
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 or Byte and the other is of type short or Short, then the type of the conditional expression is short.
If one of the operands is of type T where T is byte, short, or char, and the other operand is a constant expression (15.28 15.29) of type int 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, or Character, and the other operand is a constant expression of type int 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).
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>:
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.
switch ExpressionsA 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.
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.
switch expressionGiven 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 the case constants associated with the switch block must contain all the enum constants of that enum type.
If the switch block consists of switch labeled rules, then any switch labeled 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
switchexpression to have an empty switch block.
Switch labeled rules in
switchexpressions differ from those inswitchstatements (14.11.2). Inswitchexpressions they may be any switch labeled expression, whereas inswitchstatements they must be switch labeled statement expressions (14.8).
The result expressions of a switch expression are determined as follows:
If the switch block consists of switch labeled rules, then each is considered in turn:
if it is a switch labeled expression, then this expression is a result expression of the switch expression.
if it is a switch labeled block, then every expression immediately contained in a yield statement in the block whose yield target is the given switch expression is a result expression of the switch 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 given switch expression is a result expression of the switch 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 the switch expression.
Otherwise, if the type of each result expression is boolean or Boolean, an unboxing conversion (5.1.8) is applied to each result expression of type Boolean, and the switch expression has type boolean.
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.
switch ExpressionsWhen 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 a NullPointerException is thrown and the entire switch 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 entire switch expression completes abruptly for that reason.
If a switch label matches, then one of the following applies:
If it is the switch label for an 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 block, then the block is executed.
If it is the switch label for a throw statement, then the throw 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 the switch expression completes normally and the value of the switch expression is V.
If execution of a statement completes abruptly for any reason other than a yield with value, then the switch expression completes abruptly for the same reason.
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 not instanceof) (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
caselabels inswitchstatements andswitchexpressions (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, orforstatement 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."
switch ExpressionsSuppose 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 every yield statement with an expression e in the switch block that may exit the switch expression, V is definitely assigned after e when true.
V is definitely assigned after a switch expression when false iff for every yield statement with an expression e in the switch block that may exit the switch expression, V is definitely assigned after e when false.
V is definitely unassigned after a switch expression when true iff for every yield statement with an expression e in the switch block that may exit the switch expression, V is definitely unassigned before the yield statement and V is definitely unassigned after e when true.
V is definitely unassigned after a switch expression when false iff for every yield statement with an expression e in the switch block that may exit the switch expression, V is definitely unassigned before the yield 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 labeled rules:
V is definitely assigned after a switch expression when true iff for every switch labeled rule one of the following is true:
It is a switch labeled expression e and V is definitely assigned after e when true.
It is a switch labeled block b and for every yield statement with expression e contained in b that may exit the switch expression, V is definitely assigned after e when true.
It is a switch labeled throw statement.
V is definitely assigned after a switch expression when false iff for every switch labeled rule one of the following is true:
It is a switch labeled expression e and V is definitely assigned after e when false.
It is a switch labeled block b and for every yield statement with an expression e contained in b that may exit the switch expression, V is definitely assigned after e when false.
It is a switch labeled throw statement.
V is definitely unassigned after a switch expression when true iff for every switch labeled rule one of the following is true:
It is a switch labeled expression e and V is definitely unassigned after e when true .
It is a switch labeled block b and for every yield statement with expression e contained in b that may exit the switch expression, V is definitely unassigned before the yield statement and V is definitely unassigned after e when true.
It is a switch labeled throw statement.
V is definitely unassigned after a switch expression when false iff for every switch labeled rule one of the following is true:
It is a switch labeled expression e and V is definitely unassigned after e when false.
It is a switch labeled block b and for every yield statement with expression e contained in b that may exit the switch expression, V is definitely unassigned before the yield statement and V is definitely unassigned after e when false.
It is a switch labeled throw statement.
V is [un]assigned before any switch labeled expression or statement in the switch block iff V is [un]assigned after the selector expression.
switch ExpressionsSuppose 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 the switch statement.
For each switch labeled rule (14.11.1) in the switch block, V is [un]assigned after the expression, block, or throw statement of the switch labeled rule.
V is [un]assigned before the selector expression of a switch expression iff V is [un]assigned before the switch expression.
V is [un]assigned before the expression, block, or throw statement of a switch labeled rule of a switch expression iff V is [un]assigned after the selector expression of the switch 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.
switch expression iff V is [un]assigned after the preceding block statement.booleanSuppose 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.
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:
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.
++ 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--.
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
finalvariables. If V is a blankfinallocal variable, then only the method to which its declaration belongs can perform assignments to V. If V is a blankfinalfield, 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.
switch StatementsV 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 the switch 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 after a switch statement (14.11) iff all of the following are true:
V is [un]assigned before every break statement that may exit the switch statement.
For each switch labeled rule (14.11.1) in the switch block, V is [un]assigned after the expression, block, or throw statement of the switch labeled 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 the switch statement.
V is [un]assigned before the expression, block, or throw statement of a switch labeled rule of a switch statement iff V is [un]assigned after the selector expression of the switch statement.
V is [un]assigned before the first block statement of a switch labeled statement group of a switch statement 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.
break, yield, continue, return, and throw StatementsBy convention, we say that V is [un]assigned after any break, yield, continue, return, or throw 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, orthrowstatement never completes normally, it vacuously satisfies this notion.
In a return statement with an expression e, or a throw statement with an expression e, or a yield statement with an expression e, V is [un]assigned before e iff V is [un]assigned before the return, or throw or yield statement.
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).
...
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.
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.
ORACLE AMERICA, INC. IS WILLING TO LICENSE THIS SPECIFICATION TO YOU ONLY UPON THE CONDITION THAT YOU ACCEPT ALL OF THE TERMS CONTAINED IN THIS LICENSE AGREEMENT ("AGREEMENT"). PLEASE READ THE TERMS AND CONDITIONS OF THIS AGREEMENT CAREFULLY.
Specification: JSR-388 Java SE 13 ("Specification")
Version: 13
Status: Early Draft Review
Release: May 2019
Copyright © 1997, 2019, Oracle America, Inc.
500 Oracle Parkway, Redwood City, California 94065, U.S.A.
All rights reserved.
The Specification is protected by copyright and the information described therein may be protected by one or more U.S. patents, foreign patents, or pending applications. Except as provided under the following license, no part of the Specification may be reproduced in any form by any means without the prior written authorization of Oracle America, Inc. ("Oracle") and its licensors, if any. Any use of the Specification and the information described therein will be governed by the terms and conditions of this Agreement.
Subject to the terms and conditions of this license, including your compliance with Paragraphs 1 and 2 below, Oracle hereby grants you a fully-paid, non-exclusive, non-transferable, limited license (without the right to sublicense) under Oracle's intellectual property rights to:
Review the Specification for the purposes of evaluation. This includes: (i) developing implementations of the Specification for your internal, non-commercial use; (ii) discussing the Specification with any third party; and (iii) excerpting brief portions of the Specification in oral or written communications which discuss the Specification provided that such excerpts do not in the aggregate constitute a significant portion of the Technology.
Distribute implementations of the Specification to third parties for their testing and evaluation use, provided that any such implementation:
does not modify, subset, superset or otherwise extend the Licensor Name Space, or include any public or protected packages, classes, Java interfaces, fields or methods within the Licensor Name Space other than those required/authorized by the Specification or Specifications being implemented;
is clearly and prominently marked with the word "UNTESTED" or "EARLY ACCESS" or "INCOMPATIBLE" or "UNSTABLE" or "BETA" in any list of available builds and in proximity to every link initiating its download, where the list or link is under Licensee's control; and
includes the following notice: "This is an implementation of an early-draft specification developed under the Java Community Process (JCP) and is made available for testing and evaluation purposes only. The code is not compatible with any specification of the JCP."
The grant set forth above concerning your distribution of implementations of the specification is contingent upon your agreement to terminate development and distribution of your "early draft" implementation as soon as feasible following final completion of the specification. If you fail to do so, the foregoing grant shall be considered null and void.
No provision of this Agreement shall be understood to restrict your ability to make and distribute to third parties applications written to the Specification.
Other than this limited license, you acquire no right, title or interest in or to the Specification or any other Oracle intellectual property, and the Specification may only be used in accordance with the license terms set forth herein. This license will expire on the earlier of: (a) two (2) years from the date of Release listed above; (b) the date on which the final version of the Specification is publicly released; or (c) the date on which the Java Specification Request (JSR) to which the Specification corresponds is withdrawn. In addition, this license will terminate immediately without notice from Oracle if you fail to comply with any provision of this license. Upon termination, you must cease use of or destroy the Specification.
"Licensor Name Space" means the public class or interface declarations whose names begin with "java", "javax", "com.oracle" or their equivalents in any subsequent naming convention adopted by Oracle through the Java Community Process, or any recognized successors or replacements thereof.
No right, title, or interest in or to any trademarks, service marks, or trade names of Oracle or Oracle's licensors is granted hereunder. Oracle, the Oracle logo, and Java are trademarks or registered trademarks of Oracle America, Inc. in the U.S. and other countries.
THE SPECIFICATION IS PROVIDED "AS IS" AND IS EXPERIMENTAL AND MAY CONTAIN DEFECTS OR DEFICIENCIES WHICH CANNOT OR WILL NOT BE CORRECTED BY ORACLE. ORACLE MAKES NO REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT THAT THE CONTENTS OF THE SPECIFICATION ARE SUITABLE FOR ANY PURPOSE OR THAT ANY PRACTICE OR IMPLEMENTATION OF SUCH CONTENTS WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADE SECRETS OR OTHER RIGHTS. This document does not represent any commitment to release or implement any portion of the Specification in any product.
THE SPECIFICATION COULD INCLUDE TECHNICAL INACCURACIES OR TYPOGRAPHICAL ERRORS. CHANGES ARE PERIODICALLY ADDED TO THE INFORMATION THEREIN; THESE CHANGES WILL BE INCORPORATED INTO NEW VERSIONS OF THE SPECIFICATION, IF ANY. ORACLE MAY MAKE IMPROVEMENTS AND/OR CHANGES TO THE PRODUCT(S) AND/OR THE PROGRAM(S) DESCRIBED IN THE SPECIFICATION AT ANY TIME. Any use of such changes in the Specification will be governed by the then-current license for the applicable version of the Specification.
TO THE EXTENT NOT PROHIBITED BY LAW, IN NO EVENT WILL ORACLE OR ITS LICENSORS BE LIABLE FOR ANY DAMAGES, INCLUDING WITHOUT LIMITATION, LOST REVENUE, PROFITS OR DATA, OR FOR SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF OR RELATED TO ANY FURNISHING, PRACTICING, MODIFYING OR ANY USE OF THE SPECIFICATION, EVEN IF ORACLE AND/OR ITS LICENSORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
You will hold Oracle (and its licensors) harmless from any claims based on your use of the Specification for any purposes other than the limited right of evaluation as described above, and from any claims that later versions or releases of any Specification furnished to you are incompatible with the Specification provided to you under this license.
If this Software is being acquired by or on behalf of the U.S. Government or by a U.S. Government prime contractor or subcontractor (at any tier), then the Government's rights in the Software and accompanying documentation shall be only as set forth in this license; this is in accordance with 48 C.F.R. 227.7201 through 227.7202-4 (for Department of Defense (DoD) acquisitions) and with 48 C.F.R. 2.101 and 12.212 (for non-DoD acquisitions).
You may wish to report any ambiguities, inconsistencies or inaccuracies you may find in connection with your evaluation of the Specification ("Feedback"). To the extent that you provide Oracle with any Feedback, you hereby: (i) agree that such Feedback is provided on a non-proprietary and nonconfidential basis, and (ii) grant Oracle a perpetual, non-exclusive, worldwide, fully paid-up, irrevocable license, with the right to sublicense through multiple levels of sublicensees, to incorporate, disclose, and use without limitation the Feedback for any purpose related to the Specification and future versions, implementations, and test suites thereof.
Any action related to this Agreement will be governed by California law and controlling U.S. federal law. The U.N. Convention for the International Sale of Goods and the choice of law rules of any jurisdiction will not apply. The Specification is subject to U.S. export control laws and may be subject to export or import regulations in other countries. Licensee agrees to comply strictly with all such laws and regulations and acknowledges that it has the responsibility to obtain such licenses to export, re-export or import as may be required after delivery to Licensee.
This Agreement is the parties' entire agreement relating to its subject matter. It supersedes all prior or contemporaneous oral or written communications, proposals, conditions, representations and warranties and prevails over any conflicting or additional terms of any quote, order, acknowledgment, or other communication between the parties relating to its subject matter during the term of this Agreement. No modification to this Agreement will be binding, unless in writing and signed by an authorized representative of each party.