Chapter 14. Blocks, Statements, and Patterns (original) (raw)

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

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

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

Blocks are explained first (§14.2), both because they can appear in certain places where statements are not allowed and because one kind of statement, a local variable declaration statement (§14.4.2), must be immediately contained by a block. Local class and interface declarations (§14.3) are not statements, but must also be immediately contained by a block.

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

Every statement must be reachable in a certain technical sense (§14.22).

Sections 14.23-14.29 are unused to allow for the introduction of new kinds of statements in future.

The last section of this chapter (§14.30) describes patterns, which are used within statements and expressions to conditionally declare and initialize local variables. A pattern gives a concise description of how one value, such as an object, could be composed from one or more other values, denoted by variable declarations. Pattern matching attempts to extract one or more values from a given value, as if to decompose it, and uses the extracted values to initialize the variables declared by the pattern.

14.1. Normal and Abrupt Completion of Statements

Every statement has a normal mode of execution in which certain computational steps are carried out. The following sections describe the normal mode of execution for each kind of statement.

If all the steps are carried out as described, with no indication of abrupt completion, the statement is said to complete normally. However, certain events may prevent a statement from completing normally:

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:

The terms "complete normally" and "complete abruptly" also apply to the evaluation of expressions (§15.6). The only reason an expression can complete abruptly is that an exception is thrown, because of either a throw with a given value (§14.18) or a run-time exception or error (§11 (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.

14.2. Blocks

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

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

14.3. Local Class and Interface Declarations

A local class is a nested class (§8 (Classes)) whose declaration is immediately contained by a block (§14.2).

A local interface is a nested interface (§9 (Interfaces)) whose declaration is immediately contained by a block.

LocalClassOrInterfaceDeclaration:

The following productions are shown here for convenience:

NormalInterfaceDeclaration:

Local class and interface declarations may be intermixed freely with statements (including local variable declaration statements) in the containing block.

It is a compile-time error if a local class or interface declaration has any of the access modifiers public, protected, or private (§6.6).

It is a compile-time error if a local class or interface declaration has the modifier static (§8.1.1.4), sealed, or non-sealed (§8.1.1.2, §9.1.1.4).

It is a compile-time error if the direct superclass or a direct superinterface of a local class is sealed.

It is a compile-time error if a direct superinterface of a local interface is sealed.

A local class may be a normal class (§8.1), an enum class (§8.9), or a record class (§8.10). Every local normal class is an inner class (§8.1.3). Every local enum class and local record class is implicitly static (§8.1.1.4), and therefore not an inner class.

A local interface may be a normal interface (§9.1), but not an annotation interface (§9.6). Every local interface is implicitly static (§9.1.1.3).

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

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

Example 14.3-1. Local Class Declarations

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

class Global { class Cyclic {}

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

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

}

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

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

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

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

14.4. Local Variable Declarations

A local variable declaration declares and optionally initializes one or more local variables (§4.12.3).

LocalVariableDeclaration:

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

A local variable declaration can appear in the following locations:

The rules concerning annotation modifiers for a local variable declaration are specified in §9.7.4 and§9.7.5.

If the keyword final appears as a modifier for a local variable declaration, then the local variable is a final variable (§4.12.4).

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

It is a compile-time error if the LocalVariableType is var and any of the following are true:

Example 14.4-1. Local Variables Declared With var

The following code illustrates these rules restricting the use of var:

var a = 1; // Legal var b = 2, c = 3.0; // Illegal: multiple declarators var d[] = new int[4]; // Illegal: extra bracket pairs var e; // Illegal: no initializer var f = { 6 }; // Illegal: array initializer var g = (g = 7); // Illegal: self reference in initializer

These restrictions help to avoid confusion about the type being represented by var.

14.4.1. Local Variable Declarators and Types

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

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

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

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

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

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

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

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

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

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

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

14.4.2. Local Variable Declaration Statements

A local variable declaration statement consists of a local variable declaration.

LocalVariableDeclarationStatement:

Every local variable declaration statement is immediately contained by a block, whereas other kinds of statement (§14.5) may be immediately contained by either a block or another statement.

In the containing block, local variable declaration statements may be intermixed freely with other kinds of statements and with local class and interface declarations.

A local variable declaration statement is an executable statement. Every time it is executed, the declarators are processed in order from left to right. If a declarator has an initializer, the initializer is evaluated and its value is assigned to the variable.

If a declarator does not have an initializer, then every reference to the variable must be preceded by execution of an assignment to the variable, or a compile-time error occurs by the rules of §16 (Definite Assignment).

Each initializer (except the first) is evaluated only if evaluation of the preceding initializer completes normally.

Execution of the local variable declaration statement completes normally only if evaluation of the last initializer completes normally.

If none of the declarators in a local variable declaration statement have an initializer, then executing the statement always completes normally.

14.5. Statements

There are many kinds of statements in the Java programming language. Most correspond to statements in the C and C++ languages, but some are unique.

As in C and C++, the if statement of the Java programming language suffers from the so-called "danglingelse 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:

StatementWithoutTrailingSubstatement:

The following productions from§14.9 are shown here for convenience:

IfThenElseStatementNoShortIf:

Statements are thus grammatically divided into two categories: those that might end in anif statement that has no else clause (a "short if statement") and those that definitely do not.

Only statements that definitely do not end in a short if statement may appear as an immediate substatement before the keyword else in an if statement that does have an else clause.

This simple rule prevents the "dangling else" problem. The execution behavior of a statement with the "no short if" restriction is identical to the execution behavior of the same kind of statement without the "no short if" restriction; the distinction is drawn purely to resolve the syntactic difficulty.

14.6. The Empty Statement

An empty statement does nothing.

Execution of an empty statement always completes normally.

14.7. Labeled Statements

Statements may have label prefixes.

LabeledStatementNoShortIf:

The_Identifier_ is declared to be the label of the immediately contained_Statement_.

Unlike C and C++, the Java programming language has no goto statement; identifier statement labels are used with break or continue statements (§14.15, §14.16) appearing anywhere within the labeled statement.

The scope of a label of a labeled statement is the immediately contained_Statement_.

It is a compile-time error if the name of a label of a labeled statement is used within the scope of the label as a label of another labeled statement.

There is no restriction against using the same identifier as a label and as the name of a package, class, interface, method, field, parameter, or local variable. Use of an identifier to label a statement does not obscure (§6.4.2) a package, class, interface, method, field, parameter, or local variable with the same name. Use of an identifier as a class, interface, method, field, local variable or as the parameter of an exception handler (§14.20) does not obscure a statement label with the same name.

A labeled statement is executed by executing the immediately contained_Statement_.

If the statement is labeled by an Identifier and the contained Statement completes abruptly because of a break with the same Identifier, then the labeled statement completes normally. In all other cases of abrupt completion of the Statement, the labeled statement completes abruptly for the same reason.

Example 14.7-1. Labels and Identifiers

The following code was taken from a version of the class String and its method indexOf, where the label was originally called test. Changing the label to have the same name as the local variable i does not obscure the label in the scope of the declaration of i. Thus, the code is valid.

class Test { char[] value; int offset, count; int indexOf(TestString str, int fromIndex) { char[] v1 = value, v2 = str.value; int max = offset + (count - str.count); int start = offset + ((fromIndex < 0) ? 0 : fromIndex); i: for (int i = start; i <= max; i++) { int n = str.count, j = i, k = str.offset; while (n-- != 0) { if (v1[j++] != v2[k++]) continue i; } return i - offset; } return -1; } }

The identifier max could also have been used as the statement label; the label would not obscure the local variable max within the labeled statement.

14.8. Expression Statements

Certain kinds of expressions may be used as statements by following them with semicolons.

An expression statement is executed by evaluating the expression; if the expression has a value, the value is discarded.

Execution of the expression statement completes normally if and only if evaluation of the expression completes normally.

Unlike C and C++, the Java programming language allows only certain forms of expressions to be used as expression statements. For example, it is legal to use a method invocation expression (§15.12):

System.out.println("Hello world"); // OK

but it is not legal to use a parenthesized expression (§15.8.5):

(System.out.println("Hello world")); // illegal

Note that the Java programming language does not allow a "cast tovoid" - void is not a type - so the traditional C trick of writing an expression statement such as:

(void)... ; // incorrect!

does not work. On the other hand, the Java programming language allows all the most useful kinds of expressions in expression statements, and it does not require a method invocation used as an expression statement to invoke a void method, so such a trick is almost never needed. If a trick is needed, either an assignment statement (§15.26) or a local variable declaration statement (§14.4) can be used instead.

14.9. The if Statement

The if statement allows conditional execution of a statement or a conditional choice of two statements, executing one or the other but not both.

IfThenElseStatementNoShortIf:

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

14.9.1. The if-then Statement

Anif-then statement is executed by first evaluating the_Expression_. If the result is of type Boolean, it is subjected to unboxing conversion (§5.1.8).

If evaluation of the Expression or the subsequent unboxing conversion (if any) completes abruptly for some reason, the if-then statement completes abruptly for the same reason.

Otherwise, execution continues by making a choice based on the resulting value:

14.9.2. The if-then-else Statement

Anif-then-else statement is executed by first evaluating the_Expression_. If the result is of type Boolean, it is subjected to unboxing conversion (§5.1.8).

If evaluation of the Expression or the subsequent unboxing conversion (if any) completes abruptly for some reason, then theif-then-else statement completes abruptly for the same reason.

Otherwise, execution continues by making a choice based on the resulting value:

14.10. The assert Statement

An assertion is an assert statement containing a boolean expression. An assertion is either_enabled_ or disabled. If an assertion is enabled, execution of the assertion causes evaluation of the boolean expression and an error is reported if the expression evaluates to false. If the assertion is disabled, execution of the assertion has no effect whatsoever.

To ease the presentation, the first Expression in both forms of theassert statement is referred to as Expression1. In the second form of theassert statement, the second Expression is referred to as Expression2.

It is a compile-time error if Expression1 does not have type boolean or Boolean.

It is a compile-time error if, in the second form of the assert statement, Expression2 is void (§15.1).

An assert statement that is executed after its class or interface has completed initialization is enabled if and only if the host system has determined that the top level class or interface that lexically contains the assert statement enables assertions.

Whether a top level class or interface enables assertions is determined no later than the earliest of (i) the initialization of the top level class or interface, and (ii) the initialization of any class or interface nested in the top level class or interface. Whether a top level class or interface enables assertions cannot be changed after it has been determined.

An assert statement that is executed before its class or interface has completed initialization is enabled.

This rule is motivated by a case that demands special treatment. Recall that the assertion status of a class is set no later than the time it is initialized. It is possible, though generally not desirable, to execute methods or constructors prior to initialization. This can happen when a class hierarchy contains a circularity in its static initialization, as in the following example:

public class Foo { public static void main(String[] args) { Baz.testAsserts(); // Will execute after Baz is initialized. } } class Bar { static { Baz.testAsserts(); // Will execute before Baz is initialized! } } class Baz extends Bar { static void testAsserts() { boolean enabled = false; assert enabled = true; System.out.println("Asserts " + (enabled ? "enabled" : "disabled")); } }

Invoking Baz.testAsserts() causes Baz to be initialized. Before this can happen, Bar must be initialized. Bar's static initializer again invokes Baz.testAsserts(). Because initialization of Baz is already in progress by the current thread, the second invocation executes immediately, though Baz is not initialized (§12.4.2).

Because of the rule above, if the program above is executed without enabling assertions, it must print:

Asserts enabled Asserts disabled

A disabled assert statement does nothing. In particular, neither_Expression1_ nor Expression2 (if it is present) are evaluated. Execution of a disabled assert statement always completes normally.

An enabled assert statement is executed by first evaluating Expression1. If the result is of typeBoolean, it is subjected to unboxing conversion (§5.1.8).

If evaluation of Expression1 or the subsequent unboxing conversion (if any) completes abruptly for some reason, theassert statement completes abruptly for the same reason.

Otherwise, execution continues by making a choice based on the value of Expression1:

Typically, assertion checking is enabled during program development and testing, and disabled for deployment, to improve performance.

Because assertions may be disabled, programs must not assume that the expressions contained in assertions will be evaluated. Thus, these boolean expressions should generally be free of side effects. Evaluating such a boolean expression should not affect any state that is visible after the evaluation is complete. It is not illegal for a boolean expression contained in an assertion to have a side effect, but it is generally inappropriate, as it could cause program behavior to vary depending on whether assertions were enabled or disabled.

In light of this, assertions should not be used for argument checking in public methods. Argument checking is typically part of the contract of a method, and this contract must be upheld whether assertions are enabled or disabled.

A secondary problem with using assertions for argument checking is that erroneous arguments should result in an appropriate run-time exception (such as IllegalArgumentException,ArrayIndexOutOfBoundsException, or NullPointerException). An assertion failure will not throw an appropriate exception. Again, it is not illegal to use assertions for argument checking on public methods, but it is generally inappropriate. It is intended that AssertionError never be caught, but it is possible to do so, thus the rules for try statements should treat assertions appearing in a try block similarly to the current treatment of throw statements.

14.11. The switch Statement

The switch statement transfers control to one of several statements or expressions, depending on the value of an expression.

The Expression is called the selector expression. 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.

14.11.1. Switch Blocks

The body of both a switch statement and a switch expression (§15.28) is called a switch block. This subsection presents general rules which apply to all switch blocks, whether they appear in switch statements or switch expressions. Other subsections present additional rules which apply either to switch blocks in switch statements (§14.11.2) or to switch blocks in switch expressions (§15.28.1).

SwitchBlockStatementGroup:

A switch block can consist of either:

Every switch rule and switch labeled statement group starts with a switch label, which is either a case label or a default label. Multiple switch labels are permitted for a switch labeled statement group.

A case label has one or more case constants. Every case constant must be either a constant expression (§15.29) or the name of an enum constant (§8.9.1), or a compile-time error occurs.

Switch labels and their case constants are said to be_associated_ with the switch block. No two of the case constants associated with a switch block may have the same value, or a compile-time error occurs.

The switch block of a switch statement or a switch expression is compatible with the type of the selector expression, T, if both of the following are true:

The switch block of a switch statement or a switch expression must be compatible with the type of the selector expression, or a compile-time error occurs.

Both the execution of a switch statement (§14.11.3) and the evaluation of a switch expression (§15.28.2) need to determine if a switch label matches the value of the selector expression. To determine whether a switch label in a switch block matches a given value, the value is compared with the case constants associated with the switch block. Then:

A case label can contain several case constants. The label matches the value of the selector expression if any one of its constants matches the value of the selector expression. For example, in the following code, the case label matches if the enum variable day is either one of the enum constants shown:

switch (day) { ... case SATURDAY, SUNDAY : System.out.println("It's the weekend!"); break; ... }

null cannot be used as a case constant because it is not a constant expression. Even if case null was allowed, it would be undesirable because the code in that case would never be executed. This is due to the fact that, given a selector expression of a reference type (that is, String or a boxed primitive type or an enum type), an exception will occur if the selector expression evaluates tonull at run time. In the judgment of the designers of the Java programming language, propagating the exception is a better outcome than either having no case label match, or having the default label match.

A Java compiler is encouraged (but not required) to provide a warning if a switch statement with an enum-typed selector expression lacks a default label and lacks case labels for one or more of the enum's constants. Such a switch statement will silently do nothing if the expression evaluates to one of the missing constants.

In C and C++ the body of a switch statement can be a statement and statements with case labels do not have to be immediately contained by that statement. Consider the simple loop:

for (i = 0; i < n; ++i) foo();

where n is known to be positive. A trick known as Duff's device can be used in C or C++ to unroll the loop, but this is not valid code in the Java programming language:

int q = (n+7)/8; switch (n%8) { case 0: do { foo(); // Great C hack, Tom, case 7: foo(); // but it's not valid here. case 6: foo(); case 5: foo(); case 4: foo(); case 3: foo(); case 2: foo(); case 1: foo(); } while (--q > 0); }

Fortunately, this trick does not seem to be widely known or used. Moreover, it is less needed nowadays; this sort of code transformation is properly in the province of state-of-the-art optimizing compilers.

14.11.2. The Switch Block of a switch Statement

In addition to the general rules for switch blocks (§14.11.1), there are further rules for switch blocks in switch statements. Namely, all of the following must be true for the switch block of a switch statement, or a compile-time error occurs:

switch statements differ from switch expressions in terms of which expressions may appear to the right of an arrow (->) in the switch block, that is, which expressions may be used as switch rule expressions. In a switch statement, only a statement expression may be used as a switch rule expression, but in a switch expression, any expression may be used (§15.28.1).

14.11.3. Execution of a switch Statement

A switch statement is executed by first evaluating the selector expression. Then:

If evaluation of the selector expression completes normally and the result is non-null, and the subsequent unboxing conversion (if any) completes normally, then execution of the switch statement continues by determining if a switch label associated with the switch block matches the value of the selector expression (§14.11.1). Then:

If execution of any statement or expression in the switch block completes abruptly, it is handled as follows:

Example 14.11.3-1. Fall-Through in the switch Statement

When a selector expression matches a switch label for a switch rule, the switch rule expression or statement introduced by the switch label is executed, and nothing else. In the case of a switch label for a statement group, all the block statements in the switch block that follow the switch label are executed, including those that appear after subsequent switch labels. The effect is that, as in C and C++, execution of statements can "fall through labels."

For example, the program:

class TooMany { static void howMany(int k) { switch (k) { case 1: System.out.print("one "); case 2: System.out.print("too "); case 3: System.out.println("many"); } } public static void main(String[] args) { howMany(3); howMany(2); howMany(1); } }

contains a switch block in which the code for eachcase falls through into the code for the next case. As a result, the program prints:

many too many one too many

Fall through can be the cause of subtle bugs. If code is not to fall through case to case in this manner, then break statements can be used to indicate when control should be transferred, or switch rules can be used, as in the program:

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 } } static void howManyAgain(int k) { switch (k) { case 1 -> System.out.println("one"); case 2 -> System.out.println("two"); case 3 -> System.out.println("many"); } } public static void main(String[] args) { howMany(1); howMany(2); howMany(3); howManyAgain(1); howManyAgain(2); howManyAgain(3); } }

This program prints:

one two many one two many

14.12. The while Statement

Thewhile statement executes an Expression and a Statement repeatedly until the value of the Expression is false.

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

A while statement is executed by first evaluating the_Expression_. If the result is of type Boolean, it is subjected to unboxing conversion (§5.1.8).

If evaluation of the Expression or the subsequent unboxing conversion (if any) completes abruptly for some reason, the while statement completes abruptly for the same reason.

Otherwise, execution continues by making a choice based on the resulting value:

14.12.1. Abrupt Completion of while Statement

Abrupt completion of the contained Statement is handled in the following manner:

14.13. The do Statement

The do statement executes a Statement and an Expression repeatedly until the value of the Expression is false.

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

A do statement is executed by first executing the Statement. Then there is a choice:

Executing a do statement always executes the contained Statement at least once.

14.13.1. Abrupt Completion of do Statement

Abrupt completion of the contained Statement is handled in the following manner:

Example 14.13-1. The do Statement

The following code is one possible implementation of the toHexString method of class Integer:

public static String toHexString(int i) { StringBuffer buf = new StringBuffer(8); do { buf.append(Character.forDigit(i & 0xF, 16)); i >>>= 4; } while (i != 0); return buf.reverse().toString(); }

Because at least one digit must be generated, thedo statement is an appropriate control structure.

14.14. The for Statement

The for statement has two forms:

14.14.1. The basic for Statement

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

BasicForStatementNoShortIf:

The type of the Expression must be boolean or Boolean, or a compile-time error occurs.

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

References to a local variable declared in the ForInit part of a basic for statement from a nested class or interface, or a lambda expression, are restricted, as specified in§6.5.6.1.

14.14.1.1. Initialization of for Statement

A for statement is executed by first executing the ForInit code:

14.14.1.2. Iteration of for Statement

Next, a for iteration step is performed, as follows:

If the Expression is not present, then the only way a for statement can complete normally is by use of a break statement.

14.14.1.3. Abrupt Completion of for Statement

Abrupt completion of the contained Statement is handled in the following manner:

14.14.2. The enhanced for statement

The enhanced for statement has the form:

EnhancedForStatementNoShortIf:

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

LocalVariableDeclaration:

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

The header of the enhanced for statement declares a local variable whose name is the identifier given by VariableDeclaratorId. When the enhanced for statement is executed, the local variable is initialized, on each iteration of the loop, to successive elements of the Iterable or the array produced by the expression.

The rules for a local variable declared in the header of an enhancedfor statement are specified in §14.4, disregarding any rules in that section which apply when the_LocalVariableType_ is var. In addition, all of the following must be true, or a compile-time error occurs:

The scope and shadowing of a local variable declared in the header of an enhanced for statement is specified in §6.3 and§6.4.

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

The type T of the local variable declared in the header of the enhancedfor statement is determined as follows:

The precise meaning of the enhanced for statement is given by translation into a basic for statement, as follows:

where:

where:

For example, this code:

List<? extends Integer> l = ... for (float i : l) ...

will be translated to:

for (Iterator #i = l.iterator(); #i.hasNext(); ) { float #i0 = (Integer)#i.next(); ...

Example 14.14-1. Enhanced for And Arrays

The following program, which calculates the sum of an integer array, shows how enhanced for works for arrays:

int sum(int[] a) { int sum = 0; for (int i : a) sum += i; return sum; }

Example 14.14-2. Enhanced for And Unboxing Conversion

The following program combines the enhanced for statement with auto-unboxing to translate a histogram into a frequency table:

Map<String, Integer> histogram = ...; double total = 0; for (int i : histogram.values()) total += i; for (Map.Entry<String, Integer> e : histogram.entrySet()) System.out.println(e.getKey() + " " + e.getValue() / total); }

14.15. The break Statement

A break statement transfers control out of an enclosing statement.

There are two kinds of break statement:

A break statement with no label attempts to transfer control to the innermost enclosing switch, while, do, or for statement; this enclosing statement, which is called the break target, then immediately completes normally.

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

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

It is a compile-time error if the break target contains any method, constructor, instance initializer, static initializer, lambda expression, or switch expression that encloses the break statement. That is, there are no non-local jumps.

Execution of a break statement with no label always completes abruptly, the reason being a break with no label.

Execution of a break statement with label Identifier always completes abruptly, the reason being a break with label Identifier.

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

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

Example 14.15-1. The break Statement

In the following example, a mathematical graph is represented by an array of arrays. A graph consists of a set of nodes and a set of edges; each edge is an arrow that points from some node to some other node, or from a node to itself. In this example it is assumed that there are no redundant edges; that is, for any two nodes P and Q, where Q may be the same as P, there is at most one edge from P to Q.

Nodes are represented by integers, and there is an edge from node i to node edges[`i`][`j`] for every i and j for which the array reference edges[`i`][`j`] does not throw anArrayIndexOutOfBoundsException.

The task of the method loseEdges, given integers i and j, is to construct a new graph by copying a given graph but omitting the edge from node i to node j, if any, and the edge from node j to node i, if any:

class Graph { int[][] edges; public Graph(int[][] edges) { this.edges = edges; }

public Graph loseEdges(int i, int j) {
    int n = edges.length;
    int[][] newedges = new int[n][];
    for (int k = 0; k < n; ++k) {

edgelist: { int z; search: { if (k == i) { for (z = 0; z < edges[k].length; ++z) { if (edges[k][z] == j) break search; } } else if (k == j) { for (z = 0; z < edges[k].length; ++z) { if (edges[k][z] == i) break search; } }

        // No edge to be deleted; share this list.
        newedges[k] = edges[k];
        break edgelist;

} //search

        // Copy the list, omitting the edge at position z.
        int   m  = edges[k].length - 1;
        int[] ne = new int[m];
        System.arraycopy(edges[k], 0, ne, 0, z);
        System.arraycopy(edges[k], z+1, ne, z, m-z);
        newedges[k] = ne;

} //edgelist } return new Graph(newedges); } }

Note the use of two statement labels, edgelist and search, and the use of break statements. This allows the code that copies a list, omitting one edge, to be shared between two separate tests, the test for an edge from node i to node j, and the test for an edge from node j to node i.

14.16. The continue Statement

A continue statement may occur only in a while, do, or for statement; statements of these three kinds are called iteration statements. Control passes to the loop-continuation point of an iteration statement.

There are two kinds of continue statement:

A continue statement with no label attempts to transfer control to the innermost enclosing while, do, or for statement; this enclosing statement, which is called the continue target, then immediately ends the current iteration and begins a new one.

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; this enclosing statement, which is called the continue target, then immediately ends the current iteration and begins a new one. In this case, the continue target must be a while, do, or for statement, or 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, instance initializer, static initializer, lambda expression, or switch expression that encloses the continue statement. That is, there are no non-local jumps.

Execution of a continue statement with no label always completes abruptly, the reason being a continue with no label.

Execution of a continue statement with label Identifier always completes abruptly, the reason being a continue with label Identifier.

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

See the descriptions of the while statement (§14.12), do statement (§14.13), and for statement (§14.14) for a discussion of the handling of abrupt termination because of continue.

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

Example 14.16-1. The continue Statement

In the Graph class in§14.15, one of the break statements is used to finish execution of the entire body of the outermost for loop. This break can be replaced by a continue if the for loop itself is labeled:

class Graph { int[][] edges; public Graph(int[][] edges) { this.edges = edges; }

public Graph loseEdges(int i, int j) {
    int n = edges.length;
    int[][] newedges = new int[n][];

edgelists: for (int k = 0; k < n; ++k) { int z; search: { if (k == i) { for (z = 0; z < edges[k].length; ++z) { if (edges[k][z] == j) break search; } } else if (k == j) { for (z = 0; z < edges[k].length; ++z) { if (edges[k][z] == i) break search; } }

        // No edge to be deleted; share this list.
        newedges[k] = edges[k];
        continue edgelists;

} //search

        // Copy the list, omitting the edge at position z.
        int   m  = edges[k].length - 1;
        int[] ne = new int[m];
        System.arraycopy(edges[k], 0, ne, 0, z);
        System.arraycopy(edges[k], z+1, ne, z, m-z);
        newedges[k] = ne;
    } //edgelists
    return new Graph(newedges);
}

}

Which to use, if either, is largely a matter of programming style.

14.17. The return Statement

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

There are two kinds of return statement:

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

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

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

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

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

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

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

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

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

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

14.18. The throw Statement

A throw statement causes an exception (§11 (Exceptions)) to be thrown. The result is an immediate transfer of control (§11.3) that may exit multiple statements and multiple constructor, instance initializer, static initializer and field initializer evaluations, and method invocations until a try statement (§14.20) is found that catches the thrown value. If no such try statement is found, then execution of the thread (§17 (Threads and Locks)) that executed the throw is terminated (§11.3) after invocation of the uncaughtException method for the thread group to which the thread belongs.

The_Expression_ in a throw statement must either denote a variable or value of a reference type which is assignable (§5.2) to the type Throwable, or denote the null reference, or a compile-time error occurs.

The reference type of the Expression will always be a class type (since no interface types are assignable toThrowable) which is not parameterized (since a subclass ofThrowable cannot be generic (§8.1.2)).

At least one of the following three conditions must be true, or a compile-time error occurs:

The exception types that a throw statement can throw are specified in§11.2.2.

A throw statement first evaluates the Expression. Then:

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

If there are any enclosing try statements (§14.20) whosetry blocks contain the throw statement, then any finally clauses of those try statements are executed as control is transferred outward, until the thrown value is caught. Note that abrupt completion of a finally clause can disrupt the transfer of control initiated by a throw statement.

If a throw statement is contained in a method declaration or a lambda expression, but its value is not caught by some try statement that contains it, then the invocation of the method completes abruptly because of thethrow.

If athrow statement is contained in a constructor declaration, but its value is not caught by some try statement that contains it, then the class instance creation expression that invoked the constructor will complete abruptly because of the throw (§15.9.4).

If athrow statement is contained in a static initializer (§8.7), then a compile-time check (§11.2.3) ensures that either its value is always an unchecked exception or its value is always caught by some try statement that contains it. If at run time, despite this check, the value is not caught by some try statement that contains the throw statement, then the value is rethrown if it is an instance of classError or one of its subclasses; otherwise, it is wrapped in anExceptionInInitializerError object, which is then thrown (§12.4.2).

If athrow statement is contained in an instance initializer (§8.6), then a compile-time check (§11.2.3) ensures that either its value is always an unchecked exception or its value is always caught by some try statement that contains it, or the type of the thrown exception (or one of its superclasses) occurs in the throws clause of every constructor of the class.

By convention, user-declared throwable types should usually be declared to be subclasses of class Exception, which is a subclass of class Throwable (§11.1.1).

14.19. The synchronized Statement

Asynchronized statement acquires a mutual-exclusion lock (§17.1) on behalf of the executing thread, executes a block, then releases the lock. While the executing thread owns the lock, no other thread may acquire the lock.

The type of_Expression_ must be a reference type, or a compile-time error occurs.

A synchronized statement is executed by first evaluating the_Expression_. Then:

The locks acquired by synchronized statements are the same as the locks that are acquired implicitly by synchronized methods (§8.4.3.6). A single thread may acquire a lock more than once.

Acquiring the lock associated with an object does not in itself prevent other threads from accessing fields of the object or invoking un-synchronized methods on the object. Other threads can also usesynchronized methods or the synchronized statement in a conventional manner to achieve mutual exclusion.

Example 14.19-1. The synchronized Statement

class Test { public static void main(String[] args) { Test t = new Test(); synchronized(t) { synchronized(t) { System.out.println("made it!"); } } } }

This program produces the output:

made it!

Note that this program would deadlock if a single thread were not permitted to lock a monitor more than once.

14.20. The try statement

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

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

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

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

A try statement may havecatch clauses, also called exception handlers.

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

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

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

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

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

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

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

Each class type used in the denotation of the type of an exception parameter must be the class Throwable or a subclass of Throwable, or a compile-time error occurs.

It is a compile-time error if a type variable is used in the denotation of the type of an exception parameter.

It is a compile-time error if a union of types contains two alternatives Di and Dj (ij) where Di is a subtype of Dj (§4.10.2).

The declared type of an exception parameter that denotes its type with a single class type is that class type.

The declared type of an exception parameter that denotes its type as a union with alternatives D1 | D2 | ... | Dn is lub(D1, D2, ..., Dn).

An exception parameter of a multi-catch clause is implicitly declared final if it is not explicitly declared final.

It is a compile-time error if an exception parameter that is implicitly or explicitly declared final is assigned to within the body of the catch clause.

An exception parameter of a uni-catch clause is never implicitly declared final, but it may be explicitly declared final or be effectively final (§4.12.4).

An implicitly final exception parameter is final by virtue of its declaration, while an effectively final exception parameter is (as it were) final by virtue of how it is used. An exception parameter of a multi-catch clause is implicitly declaredfinal, so will never occur as the left-hand operand of an assignment operator, but it is not considered effectively final.

If an exception parameter is effectively final (in a uni-catch clause) or implicitly final (in a multi-catch clause), then adding an explicit final modifier to its declaration will not introduce any compile-time errors. On the other hand, if the exception parameter of a uni-catch clause is explicitly declared final, then removing thefinal modifier may introduce compile-time errors because the exception parameter, now considered to be effectively final, can no longer longer be referenced by anonymous and local class declarations in the body of the catch clause. If there are no compile-time errors, it is possible to further change the program so that the exception parameter is re-assigned in the body of the catch clause and thus will no longer be considered effectively final.

The exception types that a try statement can throw are specified in§11.2.2.

The relationship of the exceptions thrown by the try block of atry statement and caught by the catch clauses (if any) of thetry statement is specified in §11.2.3.

Exception handlers are considered in left-to-right order: the earliest possible catch clause accepts the exception, receiving as its argument the thrown exception object, as specified in §11.3.

A multi-catch clause can be thought of as a sequence of uni-catch clauses. That is, a catch clause where the type of the exception parameter is denoted as a union D1|D2|...|Dn is equivalent to a sequence of n catch clauses where the types of the exception parameters are class types D1, D2, ..., Dn respectively. In the Block of each of the n catch clauses, the declared type of the exception parameter is lub(D1, D2, ..., Dn). For example, the following code:

try { ... throws ReflectiveOperationException ... } catch (ClassNotFoundException | IllegalAccessException ex) { ... body ... }

is semantically equivalent to the following code:

try { ... throws ReflectiveOperationException ... } catch (final ClassNotFoundException ex1) { final ReflectiveOperationException ex = ex1; ... body ... } catch (final IllegalAccessException ex2) { final ReflectiveOperationException ex = ex2; ... body ... }

where the multi-catch clause with two alternatives has been translated into two uni-catch clauses, one for each alternative. A Java compiler is neither required nor recommended to compile a multi-catch clause by duplicating code in this manner, since it is possible to represent the multi-catch clause in a class file without duplication.

A finally clause ensures that the finally block is executed after the try block and any catch block that might be executed, no matter how control leaves the try block or catch block. Handling of the finally block is rather complex, so the two cases of a try statement with and without a finally block are described separately (§14.20.1, §14.20.2).

A try statement is permitted to omit catch clauses and a finally clause if it is a try-with-resources statement (§14.20.3).

14.20.1. Execution of try-catch

A try statement without a finally block is executed by first executing the try block. Then there is a choice:

Example 14.20.1-1. Catching An Exception

class BlewIt extends Exception { BlewIt() { } BlewIt(String s) { super(s); } } class Test { static void blowUp() throws BlewIt { throw new BlewIt(); }

public static void main(String[] args) {
    try {
        blowUp();
    } catch (RuntimeException r) {
        System.out.println("Caught RuntimeException");
    } catch (BlewIt b) {
        System.out.println("Caught BlewIt");
    }
}

}

Here, the exception BlewIt is thrown by the method blowUp. The try-catch statement in the body of main has two catch clauses. The run-time type of the exception is BlewIt which is not assignable to a variable of type RuntimeException, but is assignable to a variable of type BlewIt, so the output of the example is:

Caught BlewIt

14.20.2. Execution of try-finally and try-catch-finally

A try statement with a finally block is executed by first executing the try block. Then there is a choice:

Example 14.20.2-1. Handling An Uncaught Exception With finally

class BlewIt extends Exception { BlewIt() { } BlewIt(String s) { super(s); } } class Test { static void blowUp() throws BlewIt { throw new NullPointerException(); } public static void main(String[] args) { try { blowUp(); } catch (BlewIt b) { System.out.println("Caught BlewIt"); } finally { System.out.println("Uncaught Exception"); } } }

This program produces the output:

Uncaught Exception Exception in thread "main" java.lang.NullPointerException at Test.blowUp(Test.java:7) at Test.main(Test.java:11)

The NullPointerException (which is a kind of RuntimeException) that is thrown by method blowUp is not caught by the try statement in main, because a NullPointerException is not assignable to a variable of type BlewIt. This causes the finally clause to execute, after which the thread executing main, which is the only thread of the test program, terminates because of an uncaught exception, which typically results in printing the exception name and a simple backtrace. However, a backtrace is not required by this specification.

The problem with mandating a backtrace is that an exception can be created at one point in the program and thrown at a later one. It is prohibitively expensive to store a stack trace in an exception unless it is actually thrown (in which case the trace may be generated while unwinding the stack). Hence we do not mandate a back trace in every exception.

14.20.3. try-with-resources

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

TryWithResourcesStatement:

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

LocalVariableDeclaration:

See §8.3 for UnannType.

The resource specification denotes the resources of the try-with-resources statement, either by declaring local variables with initializer expressions or by referring to existing variables. An existing variable is referred to by an expression name (§6.5.6) or a field access expression (§15.11).

The rules for a local variable declared in a resource specification are specified in §14.4. In addition, all of the following must be true, or a compile-time error occurs:

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

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

The type of a local variable declared in a resource specification is specified in §14.4.1.

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

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

Resources are final, in that:

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

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

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

14.20.3.1. Basic try-with-resources

A try-with-resources statement with no catch clauses or finally clause is called a basic try-with-resources statement.

If a basic try-with-resources statement is of the form:

try (VariableAccess ...) Block

then the resource is first converted to a local variable declaration by the following translation:

try (T #r = VariableAccess ...) { Block }

T is the type of the variable denoted by_VariableAccess_ and #r is an automatically generated identifier that is distinct from any other identifiers (automatically generated or otherwise) that are in scope at the point where the try-with-resources statement occurs. Thetry-with-resources statement is then translated according to the rest of this section.

The meaning of a basic try-with-resources statement of the form:

try ({VariableModifier} R Identifier = Expression ...) Block

is given by the following translation to a local variable declaration and a try-catch-finally statement:

{ final {VariableModifierNoFinal} R Identifier = Expression; Throwable #primaryExc = null;

try _ResourceSpecificationtail_
    _Block_
catch (Throwable #t) {
    #primaryExc = #t;
    throw #t;
} finally {
    if (_Identifier_ != null) {
        if (#primaryExc != null) {
            try {
                _Identifier_.close();
            } catch (Throwable #suppressedExc) {
                #primaryExc.addSuppressed(#suppressedExc);
            }
        } else {
            _Identifier_.close();
        }
    }
}

}

{VariableModifierNoFinal} is defined as {VariableModifier} without final, if present.

#t, #primaryExc, and #suppressedExc are automatically generated identifiers that are distinct from any other identifiers (automatically generated or otherwise) that are in scope at the point where the try-with-resources statement occurs.

If the resource specification indicates one resource, then ResourceSpecification_tail is empty (and thetry-catch-finally statement is not itself a try-with-resources statement).

If the resource specification indicates n > 1 resources, then ResourceSpecification_tail consists of the 2nd, 3rd, ..., n'th resources indicated in the resource specification, in the same order (and the try-catch-finally statement is itself a try-with-resources statement).

Reachability and definite assignment rules for the basictry-with-resources statement are implicitly specified by the translation above.

In a basic try-with-resources statement that manages a single resource:

In a basic try-with-resources statement that manages multiple resources:

14.20.3.2. Extended try-with-resources

A try-with-resources statement with at least one catch clause and/or a finally clause is called an extended try-with-resources statement.

The meaning of an extended try-with-resources statement:

try ResourceSpecification Block [Catches] [Finally]

is given by the following translation to a basic try-with-resources statement nested inside a try-catch or try-finally ortry-catch-finally statement:

try { try ResourceSpecification Block } [Catches] [Finally]

The effect of the translation is to put the resource specification "inside" the try statement. This allows a catch clause of an extended try-with-resources statement to catch an exception due to the automatic initialization or closing of any resource.

Furthermore, all resources will have been closed (or attempted to be closed) by the time the finally block is executed, in keeping with the intent of the finally keyword.

14.21. The yield Statement

A yield statement transfers control by causing an enclosingswitch expression (§15.28) to produce a specified value.

A yield statement attempts to transfer control to the innermost enclosing switch expression; this enclosing 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, instance initializer, static initializer, or lambda expression that encloses the yield statement. That is, there are no non-local jumps.

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, then, that a yield statement always completes abruptly.

Example 14.21-1. The yield Statement

In the following example, a yield statement is used to produce a value for the enclosing switch expression.

class Test { enum Day { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }

public int calculate(Day d) {
    return switch (d) {
        case SATURDAY, SUNDAY -> d.ordinal();
        default -> {
            int len = d.toString().length();
            yield len*len;
        }
    };
}

}

14.22. Unreachable Statements

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

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

For example, a Java compiler will accept the code:

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

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

The rules in this section define two technical terms:

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

Two further technical terms are used:

The rules are as follows:

One might expect the if statement to be handled in the following manner:

This approach would be consistent with the treatment of other control structures. However, in order to allow the if statement to be used conveniently for "conditional compilation" purposes, the actual rules differ.

As an example, the following statement results in a compile-time error:

while (false) { x=3; }

because the statement x=3; is not reachable; but the superficially similar case:

if (false) { x=3; }

does not result in a compile-time error. An optimizing compiler may realize that the statement x=3; will never be executed and may choose to omit the code for that statement from the generated class file, but the statement x=3; is not regarded as "unreachable" in the technical sense specified here.

The rationale for this differing treatment is to allow programmers to define "flag" variables such as:

static final boolean DEBUG = false;

and then write code such as:

if (DEBUG) { x=3; }

The idea is that it should be possible to change the value of DEBUG from false to true or fromtrue to false and then compile the code correctly with no other changes to the program text.

Conditional compilation comes with a caveat. If a set of classes that use a "flag" variable - or more precisely, any static constant variable (§4.12.4) - are compiled and conditional code is omitted, it does not suffice later to distribute just a new version of the class or interface that contains the definition of the flag. The classes that use the flag will not see its new value, so their behavior may be surprising. In essence, a change to the value of a flag is binary compatible with pre-existing binaries (no LinkageError occurs) but not behaviorally compatible.

Another reason for "inlining" values of static constant variables is because of switch statements. They are the only kind of statement that relies on constant expressions, namely that each case label of a switch statement must be a constant expression whose value is different than every other case label. case labels are often references to static constant variables so it may not be immediately obvious that all the labels have different values. If it is proven that there are no duplicate labels at compile time, then inlining the values into the class file ensures there are no duplicate labels at run time either - a very desirable property.

Example 14.22-1. Conditional Compilation

If the example:

class Flags { static final boolean DEBUG = true; } class Test { public static void main(String[] args) { if (Flags.DEBUG) System.out.println("DEBUG is true"); } }

is compiled and executed, it produces the output:

DEBUG is true

Suppose that a new version of class Flags is produced:

class Flags { static final boolean DEBUG = false; }

If Flags is recompiled but not Test, then running the new binary with the existing binary of Test produces the output:

DEBUG is true

because DEBUG is a static constant variable, so its value could have been used in compiling Test without making a reference to the class Flags.

This behavior would also occur if Flags was an interface, as in the modified example:

interface Flags { boolean DEBUG = true; } class Test { public static void main(String[] args) { if (Flags.DEBUG) System.out.println("DEBUG is true"); } }

In fact, because the fields of interfaces are alwaysstatic and final, we recommend that only constant expressions be assigned to fields of interfaces. We note, but do not recommend, that if a field of primitive type of an interface may change, its value may be expressed idiomatically as in:

interface Flags { boolean debug = Boolean.valueOf(true).booleanValue(); }

ensuring that this value is not a constant expression. Similar idioms exist for the other primitive types.

14.30. Patterns

A pattern describes a test that can be performed on a value. Patterns appear as operands of statements and expressions, which provide the values to be tested. Patterns declare local variables, known as pattern variables.

The process of testing a value against a pattern is known as_pattern matching_. If a value successfully matches a pattern, then the process of pattern matching initializes the pattern variable declared by the pattern.

Pattern variables are only in scope (§6.3) where pattern matching succeeds and thus the pattern variables will have been initialized. It is not possible to use a pattern variable that has not been initialized.

14.30.1. Kinds of Patterns

A type pattern is used to test whether a value is an instance of the type appearing in the pattern.

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

LocalVariableDeclaration:

See §8.3 for UnannType.

A type pattern declares one local variable, known as a pattern variable. The Identifier in the local variable declaration specifies the name of the pattern variable.

The rules for a local variable declared in a type pattern are specified in §14.4. In addition, all of the following must be true, or a compile-time error occurs:

The type of a pattern variable is the reference type denoted by_LocalVariableType_.

The type of a type pattern is the type of its pattern variable.

An expression e is compatible with a pattern of type T if e is downcast compatible with T (§5.5).

Compatibility of an expression with a pattern is used by the instanceof pattern match operator (§15.20.2).

14.30.2. Pattern Matching

Pattern matching is the process of testing a value against a pattern at run time. Pattern matching is distinct from statement execution (§14.1) and expression evaluation (§15.1).

The rules for determining whether a value matches a pattern, and for initializing pattern variables, are as follows:

There is no rule to cover a value that_is_ the null reference. This is because the solitary construct that performs pattern matching, the instanceof pattern match operator (§15.20.2), only does so when a value is not the null reference. It is possible that future versions of the Java programming language will allow pattern matching in other expressions and statements.