This document describes changes to the Java Language Specification to support Pattern Matching for switch
, a preview feature of Java SE 18. See JEP 420 for an overview of the feature.
Changes are described with respect to existing sections of the JLS. New text is indicated like this and deleted text is indicated like this. Explanation and discussion, as needed, is set aside in grey boxes.
Changelog:
2021-12-08: Small typos corrected.
2021-11-11: Small changes following feedback:
14.11.1 Improved definitions of exhaustiveness of switch blocks
13.4.2.1, 13.4.26 and 13.5.2: Added new text explaining how changes to sealed hierarchies and enum classes can cause exceptions when executing a pre-compiled exhaustive switch.
2021-10-20: First draft released. Main changes from JEP 405 preview feature, in addition to various bug-fixes, are:
Chapter 6: Names
6.3 Scope of a Declaration
6.3.1 Scope for Pattern Variables in Expressions
6.3.1.6 switch
Expressions
The following rule applies rules apply to a switch
expression (15.28) with a switch block consisting of switch rules (14.11.1):
A pattern variable introduced by a switch label is definitely matched in the associated switch rule expression, switch rule block or switch rule
throw
statement.It is a compile-time error if any pattern variable introduced by a switch label is already in scope at the associated switch rule expression, switch rule block or switch rule
throw
statement.
The following rules apply to a switch expression with a switch block consisting of switch labeled statement groups (14.11.1):
A pattern variable introduced by a switch label is definitely matched in all the statements of the switch labeled statement group.
It is a compile-time error if any pattern variable introduced by a switch label is already in scope at the statements of the switch labeled statement group.
- A pattern variable introduced by a statement S contained in a switch labeled statement group
(14.11.1)is definitely matched at all the statements following S, if any, in the switch labeled statement group.
6.3.2 Scope for Pattern Variables in Statements
6.3.2.6 switch
Statements
The following rule applies rules apply to a switch
statement (14.11) with a switch block consisting of switch rules (14.11.1):
A pattern variable introduced by a switch label is definitely matched in the associated switch rule expression, switch rule block or switch rule
throw
statement.It is a compile-time error if any pattern variable introduced by a switch label is already in scope at the associated switch rule expression, switch rule block or switch rule
throw
statement.
The following rules apply to a switch expression with a switch block consisting of switch labeled statement groups (14.11.1):
A pattern variable introduced by a switch label is definitely matched in all the statements of the switch labeled statement group.
It is a compile-time error if any pattern variable introduced by a switch label is already in scope at the statements of the switch labeled statement group.
- A pattern variable introduced by a labeled statement S contained in a switch block statement group
(14.11.1)is definitely matched at all the statements following S, if any, in the switch block statement group.
6.3.3 Scope for Pattern Variables in Patterns
6.3.3.1 Guarded Pattern
The following rule applies to a guarded pattern p && e
:
A pattern variable declared by
p
is definitely matched ine
.It is a compile-time error if a pattern variable declared by
p
is already in scope ate
.
6.3.4 Scope for Pattern Variables in Switch Labels
Pattern variables can be introduced by switch labels that have patterns, and are in scope for the relevant parts of the associated switch
expression (6.3.1.6) or switch
statement (6.3.2.6).
The following rule applies to a switch label:
- A pattern variable is introduced by a switch label that has a pattern case label element with pattern p if it is declared by p.
Chapter 13: Binary Compatibility
13.4 Evolution of Classes
13.4.2 sealed
, non-sealed
, and final
Classes
13.4.2.1 sealed
Classes
If a class that was freely extensible (8.1.1.2) is changed to be declared sealed
, then an IncompatibleClassChangeError
is thrown if a binary of a pre-existing subclass of this class is loaded and is not a permitted direct subclass of this class (8.1.6); such a change is not recommended for widely distributed classes.
Changing a class that was declared final
to be declared sealed
does not break compatibility with pre-existing binaries.
Adding a class to the set of permitted direct subclasses of a sealed
class will not break compatibility with pre-existing binaries.
Note that evolving a
sealed
class by adding a permitted direct subclass is considered a binary compatible change because pre-existing binaries that previously linked without error (e.g., a class file that contains an exhaustiveswitch
(14.11.1)) will continue to link without error. A class file that contains an exhaustiveswitch
will not fail to link if thesealed
class that it switches over is expanded by the hierarchy's owner to have a new permitted direct subclass. The JVM is not required to perform exhaustiveness checks when linking a class file that contains an exhaustiveswitch
.
The execution of an exhaustive
switch
can fail with a linkage error (anIncompatibleClassChangeError
is thrown) if it encounters an instance of a permitted direct subclass that was not known at compile time (14.11.3, 15.28.2). Strictly speaking, the linkage error is not flagging a binary incompatible change of thesealed
class, but more accurately a migration incompatible change of thesealed
class.
If a class is removed from the set of permitted direct subclasses of a sealed
class, then an IncompatibleClassChangeError
is thrown if the pre-existing binary of the removed class is loaded.
Deleting the sealed
modifier from a class that does not have a sealed
direct superclass or a sealed
direct superinterface does not break compatibility with pre-existing binaries.
If a sealed class C did have a
sealed
direct superclass or asealed
direct superinterface, then deleting thesealed
modifier would prevent C from being recompiled, as every class with asealed
direct superclass or asealed
direct superinterface must be eitherfinal
,sealed
, ornon-sealed
.
13.4.26 Evolution of Enum Classes
Adding or reordering enum constants in an enum class will not break compatibility with pre-existing binaries.
As with
sealed
classes (13.4.2.1), whilst adding an enum constant to an enum class is considered a binary compatible change, it may cause the execution of an exhaustiveswitch
(14.11.1) to fail with a linkage error (anIncompatibleClassChangeError
may be thrown) if theswitch
encounters the new enum constant that was not known at compile time (14.11.3, 15.28.2).
Deleting an enum constant from an enum class will delete the public
field that corresponds to the enum constant (8.9.3). The consequences are specified in 13.4.8. Such a change is not recommended for widely distributed enum classes.
In all other respects, the binary compatibility rules for enum classes are identical to those for normal classes.
13.5 Evolution of Interfaces
13.5.2 sealed
and non-sealed
Interfaces
If an interface that was freely extensible (9.1.1.4) is changed to be declared sealed
, then an IncompatibleClassChangeError
is thrown if a binary of a pre-existing subclass or subinterface of this interface is loaded and is not a permitted direct subclass or subinterface of this interface (9.1.4); such a change is not recommended for widely distributed classes.
Adding a class or interface to the set of permitted direct subclasses or subinterfaces, respectively, of a sealed
interface will not break compatibility with pre-existing binaries.
As with
sealed
classes (13.4.2.1), whilst adding a permitted direct subclass or subinterface of asealed
interface is considered a binary compatible change, it may cause the execution of an exhaustiveswitch
(14.11.1) to fail with a linkage error (anIncompatibleClassChangeError
may be thrown) if theswitch
encounters an instance of the new permitted direct subclass and subinterface that was not known at compile time (14.11.3, 15.28.2).
If a class or interface is removed from the set of permitted direct subclasses or subinterfaces of a sealed
interface, then an IncompatibleClassChangeError
is thrown if the pre-existing binary of the removed class or interface is loaded.
Changing an interface that was declared sealed
to be declared non-sealed
does not break compatibility with pre-existing binaries.
A
non-sealed
interface I must have asealed
direct superinterface. Deleting thenon-sealed
modifier would prevent I from being recompiled, as every interface with asealed
direct superinterface must besealed
ornon-sealed
.
Deleting the sealed
modifier from an interface that does not have a sealed
direct superinterface does not break compatibility with pre-existing binaries.
If a sealed interface I did have a
sealed
direct superinterface, then deleting thesealed
modifier would prevent I from being recompiled, as every interface with asealed
direct superinterface must besealed
ornon-sealed
.
Chapter 14: Blocks, Statements, and Patterns
14.11 The switch
Statement
The switch
statement transfers control to one of several statements or expressions, depending on the value of an expression.
- SwitchStatement:
switch
(
Expression)
SwitchBlock
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.
These restrictions on the type of the selector expression are now included in the notion of a switch block being compatible with a selector expression, defined in the following section.
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).
- SwitchBlock:
{
SwitchRule {SwitchRule}}
{
{SwitchBlockStatementGroup} {SwitchLabel:
}}
- SwitchRule:
- SwitchLabel
->
Expression;
- SwitchLabel
->
Block - SwitchLabel
->
ThrowStatement - SwitchBlockStatementGroup:
- SwitchLabel
:
{SwitchLabelBlockStatements:
}
- SwitchLabel:
case
CaseConstant {,
CaseConstant}default
- SwitchLabel:
- CaseOrDefaultLabel {
:
CaseOrDefaultLabel } - CaseOrDefaultLabel:
case
CaseLabelElement {,
CaseLabelElement }default
- CaseLabelElement:
- CaseConstant
- Pattern
null
default
- CaseConstant:
- ConditionalExpression
A switch block can consist of either:
Switch rules, which use
->
to introduce either a switch rule expression, a switch rule block, or a switch rulethrow
statement; orSwitch labeled statement groups, which use
:
to introduce switch labeled block statements.
Every switch rule and switch labeled statement group starts with a switch label, which is either a uses one or more case
label or a default
label.case
or default
labels. A case
label has one or more case label elements. Multiple switch labels are permitted for a switch labeled statement group. A switch label has a case label element, if it uses a case
label that has that case label element.
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.
If a switch label appears at the end of a switch block, it is a compile-time error if it consists of more than one case
or default
label.
It is a compile-time error if the switch label of a switch rule consists of more than one case
or default
label.
This means that
case 1: case 2 -> ...
is not a valid switch rule, but can be written ascase 1, 2 -> ...
.
For every switch label in a switch block, all of the following must be true, otherwise a compile-time error occurs:
If a switch label has a constant case label element then it is either a constant expression (15.29) or the name of an enum constant (8.9.1).
A switch label may not have more than one constant case label element with the same value.
If a switch label has a constant case label element then if the switch label also has other case element labels they must be either a constant case label element, the
default
case label element, or thenull
case label element.A switch label may not use more than one
default
label.A switch label may not have more than one
default
case label element.A switch label may not have both a
default
case label element and use adefault
label.A switch label may not have more than one
null
case label element.If a switch label has a
null
case label element then if the switch label also has any pattern case element labels, they must be type patterns (14.30.1).A switch label may not have more than one pattern case label element.
A switch label may not both have a pattern case label element and use a
default
label.A switch label may not have both a pattern case label element and a
default
case label element.
These rules restrict the form of switch labels. Much of the complication is due to supporting the two ways of combining case label elements in switch labels for statement groups (for example
case 1: case 2
andcase 1,2
).
A switch label is called a default switch label if it either uses a default
label or has a default
case label element.
A switch label is said to dominate another switch label if there are values for which both apply and there is not an obvious preference. The rules for determining dominance are as follows:
A switch label that has a constant case label element dominates another switch label that has the same constant case label element.
This rules out examples such as the following where it would otherwise be unclear which switch label matches when the value of the selector expression is
2
.int i = ...; switch (i) { case 1, 2 -> System.out.println("1 or 2"); case 2, 3 -> System.out.println("2 or 3"); // Error! }
A switch label that has a pattern case label element p dominates another switch label that has a pattern case label element q if p dominates q (14.30.3).
The definition of a pattern dominating another pattern is based on their type. For example, the following results in a compile-time error:
Object o = ... switch (o) { case Object obj -> System.out.println("Object"); case String s -> System.out.println("String"); // Error - dominated switch label }
More precisely, dominance is defined in terms of the erasure of the types of the patterns. For example, the type pattern
List<String> ls
dominates the type patternList<Integer> li
and vice versa. This means that, for example, the following switch block results in a compile-time error:Object o = ...; switch (o) { case List<Integer> li -> ... case List<String> ls -> ... // Error - dominated switch label ... }
Dominance permits a guarded pattern to be followed by its unguarded form:
Object o = ...; switch (o) { case List l && l.length() > 2 -> ... case List l -> ... // List of length <= 2 ... }
A switch label that has a pattern case label element p that is total for the type of the selector expression of the enclosing
switch
statement orswitch
expression dominates a switch label that has anull
case label element.A switch label that has a pattern case label element p dominates another switch label that has a constant case label element c if p dominates c.
A pattern p dominates a constant c if one of the following is true:
p is a type pattern and either of the following is true:
the type of c is a primitive type and its wrapper class (5.1.7) is a subtype of the erasure of the type of p.
the type of c is a reference type and is a subtype of the erasure of the type of p.
The first part of this rule covers cases where a primitive constant case label is dominated:
Object o = ...; switch (o) { case Integer i -> ... case 42 -> ... // Error - dominated! ... }
The second part of this rule covers cases where a enum constant case label is dominated:
enum E { A, B, C } Object o = ...; switch (o) { case E e -> ... case A -> ... // Error - dominated! ... }
p is a parenthesized pattern and its contained pattern dominates c.
p is a guarded pattern and its contained pattern dominates c.
A guarded pattern case label dominates a constant case label if its contained pattern dominates. No analysis - undecidable in general - of the guarding expression is undertaken. Thus constant case labels should appear before any guarded pattern case labels, for example:
Object o = ...; switch (o) { case 42 -> ... case Integer i && i < 50 -> ... // All integers less than 50 // *except* 42 case Integer i -> ... // All other integers ... }
It is a compile-time error if a switch label in a switch block dominates any switch label that follows it in the switch block.
It is a compile-time error if there is a statement in a switch block that consists of switch-labeled statement groups for which both of the following are true:
It is labeled with a switch label that has a pattern case label element whose pattern introduces a pattern variable.
There is a statement preceding it in the switch block and that statement can completely normally (14.22).
This condition is required to exclude the possibility of a switch labeled statement being reached for which a pattern variable declared in its switch label is in scope but without the pattern matching having succeeded. For example, the statement labeled by the switch label that has the type pattern
Integer i
could be reached from the preceding statement group, and so the pattern variablei
will not be initialized:Object o = "Hello"; switch (o) { case String s: System.out.println("String: " + s ); case Integer i: System.out.println(i + 1); // Error! Can be reached // without matching switch label }
The switch block of a switch
statement or a switch
expression is compatible with the type of the selector expression, T e, if both of the following are true all the case
labels used by the switch labels in the switch block are compatible with e. A case
label is compatible with e if every case label element it has is compatible with e, as follows:
A constant case label element c is compatible with e if exactly one of the following is true:
If the type of e, T, is
not an enum type,char
,byte
,short
,int
,Character
,Byte
,Short
,Integer
, orString
, then ceveryis assignment compatible with T (5.2).case
constant associated with the switch blockIf the type of e, T, is an enum type, then c
everyis an enum constant of type T.case
constant associated with the switch block
A
null
case label element is compatible with e if the type of e is a reference type.A
default
case label element is always compatible with e.A pattern case label element p is compatible with e if e is compatible with p (14.30.1).
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.
It is a compile-time error if both of the following are true for a switch
expression or a switch
statement:
There is a default switch label in the switch block, and
There is a switch label in the switch block that has a pattern case label element whose pattern is total for the type of the selector expression (14.30.3).
A pattern that is total for the type of the selector expression will match every value, and so behaves much like a default switch label.
A type T supports a sealed
class or interface named C if and only if one of the following holds:
T is a class type that names C, and the class C is both
sealed
andabstract
.T is an interface type that names C, and the interface C is
sealed
.T is a type variable, and its upper bound supports C.
T is an intersection type T1
&
...&
Tn, and a type Ti supports C (1 ≤ i ≤ n).
The switch block of switch
expression or switch
statement is exhaustive for a selector expression of type T if one of the following is true:
T names an enum class E and all of the enum constants of E appear as constant switch label elements in the switch block.
Note that a default switch label is permitted, but not required in the case where all the enum constants appear in the switch labels. For example:
enum E { F, G, H } static int testEnumCoverage(E e) { return switch(e) { case F -> 0; case G -> 1; case H -> 2; // No default required! }; }
T supports a
sealed
class or interface named C, and the switch block is exhaustive for all the permitted direct subclasses and subinterfaces of C that are applicable at type T.A class or interface named C is applicable at a type U if one of the following holds:
C is not a generic class or interface and there is a casting conversion from U to C (5.5).
C is a generic class or interface with type parameters F1...Fn and there is a casting conversion from U to the type C<X1, ..., Xn> where X1, ..., Xn are distinct type arguments that are not in scope in the body of C.
Note that a default switch label is permitted, but not required in the case where the switch block is exhaustive for all the permited direct subclasses and subinterfaces of a
sealed
class or interface. For example:sealed interface I permits A, B, C {} final class A implements I {} final class B implements I {} record C(int j) implements I {} // Implicitly final static int testExhaustive1(I i) { return switch(i) { case A a -> 0; case B b -> 1; case C c -> 2; // No default required! }; }
The fact that a permitted direct subclass or subinterface may only extend a particular instantiation of a generic
sealed
superclass or superinterface means that it may not be applicable when considering whether a switch block is exhaustive. For example:sealed interface J<X> permits D, E {} final class D implements J<String> {} final class E<X> implements J<X> {} static int testExhaustive2(J<Integer> ji) { return switch(ji) { // Exhaustive! case E<Integer> e -> 42; }; }
As the selector expression has type
J<Integer>
the permitted direct subclassD
is not applicable as there is no possibility that the value ofji
can be an instance ofD
. Thus, the switch block is exhaustive as it covers the only applicable permitted direct subclass,E
.A switch label in the switch block has a pattern case label element p where the pattern p is total for T (14.30.3).
There is a default switch label in the switch block.
A switch
statement or expression is exhaustive if its switch block is exhaustive for the type of the selector expression.
As the meaning of some patterns is determined by the type of the expression that are being matching against, patterns appearing in switch labels must be resolved (14.30.2).
Resolving a switch block at type T results in an identical switch block except where every switch label L is replaced with the result of resolving L at type T.
Resolving a switch label L at type T proceeds by resolving all the default
and case
labels it uses as follows:
If L uses a
default
label then the resolved switch label uses adefault
label.If L uses a
case
label then the resolved switch label uses acase
label with the case label elements determined as follows:All constant case label elements appearing in L appear in the resolved switch label.
All
default
case label elements appearing in L appear in the resolved switch label.If a
null
case label element and no pattern case element labels appear in L then anull
case label element appears in the resolved switch label.If no
null
case label element appears in L then for every pattern case label element p appearing in L, a pattern case element label q appears in the resolved switch label, where q is the result of resolving pattern p at type T.The definition of resolving a pattern at a type is given in 14.30.2.
If a
null
case label element appears in L then for every pattern case label element appearing in L that is a type pattern declaring a pattern variable x of type U, a pattern case element q appears in the resolved switch label, where q is an any pattern that declares x of type U.All pattern case element labels appearing in a switch label that also contains a
null
case element label are required to be type patterns (14.11.1).This final rule covers an important aspect of resolving a switch label. If a switch label has both
null
and type pattern case label elements, then these are resolved to a single pattern case label element that is an any pattern. This ensures that the initialization of the pattern variable declared by the type pattern is always handled by the semantics of pattern matching, even when the value being matched isnull
.
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 in a resolved switch block matches applies to the value of the selector expression. To determine Determining whether a switch label in a resolved switch block matches applies to a given value, the value is compared with the is as follows: case
constants associated with the switch blockThen:
If the value is the null reference, then we determine the first (if any) switch label in the switch block that applies to the value as follows:
A switch label that has a
null
case label element applies.A switch label that has a pattern case label element p applies if the value matches p (14.30.2).
If the value is not the null reference, then we determine the first (if any) switch label in the switch block that applies to the value as follows:
A switch label that has a constant case label element c applies if both of the following are true:
If the type of the value is
Character
,Byte
,Short
, orInteger
, then the value is first subjected to unboxing conversion (5.1.8). If this conversion completes abruptly, then we say that applying completes abruptly for the same reason. If this conversion completes normally then the switch label applies if the constant c is equal to the unboxed value.If one of the.case
constants is equal to the value, then we say that thecase
label which contains thecase
constant matchesIf the type of the value is not
Character
,Byte
,Short
, orInteger
, then the switch label applies if the constant c is equal to the value.
EqualityIn both cases, equality is defined in terms of the==
operator (15.21) unless the value is aString
, in which case equality is defined in terms of theequals
method of classString
.A switch label that has a pattern case label element p applies if the value matches the pattern p (14.30.2). If pattern matching completes abruptly then applying completes abruptly for the same reason.
If no switch
label that is not a default switch labelcase
matchesapplies
but there is adefault switch label, then thedefault
default switch label applies.default
A switch block may have at most one default switch label.
A
switch label cancase
containhave severalconstant case label elements. The labelcase
constantsmatchesapplies to the value of the selector expression if any one of its constantsmatchesis equal to the value of the selector expression. For example, in the following code, theswitch label matches if the enum variablecase
day
is either one of the enum constants shown:switch (day) { ... case SATURDAY, SUNDAY : System.out.println("It's the weekend!"); break; ... }
If a switch label that has a pattern case label element applies, then this is because the process of pattern matching the value against the pattern has succeeded (14.30.2). If a value successfully matches a pattern then the process of pattern matching initializes any pattern variables declared by the pattern.
null
cannot be used as acase
constant because it is not a constant expression. Even ifcase
null
was allowed, it would be undesirable because the code in thatcase
can never be executed. Namely, if the selector expression is of a reference type (that is,String
or a boxed primitive type or an enum type), then 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 nocase
label match, or having thedefault
label match.
In C and C++ the body of a
switch
statement can be a statement and statements withcase
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.
An enhanced switch
statement is one where either (i) the type of the selector expression is not char
, byte
, short
, int
, Character
, Byte
, Short
, Integer
, String
, or an enum type, or (ii) at least one of the switch labels has a pattern case label element or a null
case label element.
Namely, all All of the following must be true for the switch block of a switch
statement, or a compile-time error occurs:
No more than onedefault
label is associated with theswitch
block.Every switch rule expression in the switch block is a statement expression (14.8).
switch
statements differ fromswitch
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 aswitch
statement, only a statement expression may be used as a switch rule expression, but in aswitch
expression, any expression may be used (15.28.1).If the
switch
statement is an enhancedswitch
statement, then it must be exhaustive.
Prior to Java SE 17,
switch
statements (andswitch
expressions) were limited in two ways: (i) the type of the selector expression was restricted to an integral type, an enum type, orString
; and (ii) only constant case label elements were supported. Moreover, unlikeswitch
expressions,switch
statements did not have to be exhaustive. This is often the cause of difficult to detect bugs, where no switch label applies and theswitch
statement will silently do nothing. For example:enum E { A, B, C} E e = ...; switch (e) { case A -> System.out.println("A"); case B -> System.out.println("B"); // No case for C! }
With Java SE 17,
switch
statements have been enhanced in the sense that the two limitations listed above have been lifted. The designers of the Java programming language decided that enhancedswitch
statements should align withswitch
expressions and be required to be exhaustive. This is often achieved with the addition of a trivial default switch label. For example, the following enhancedswitch
statement is not exhaustive:Object o = ...; switch (o) { // Error - non-exhaustive switch! case String s -> System.out.println("A string!"); }
but it can easily be made exhaustive:
Object o = ...; switch (o) { case String s -> System.out.println("A string!"); default -> {} }
For compatibility reasons,
switch
statements that are not an enhancedswitch
statement are not required to be exhaustive.One consequence of requiring exhaustiveness is that an enhanced
switch
statement, unlike a normalswitch
statement, can not have an empty switch block.
14.11.3 Execution of a switch
Statement
Execution of a switch
statement is always with respect to a resolved switch block (14.11.1). The switch block is resolved at type T, where T is the type of the selector expression.
A switch
statement is executed by first evaluating the selector expression. Then:
If evaluation of the selector expression completes abruptly, then the entire
switch
statement completes abruptly for the same reason.Otherwise, if the result of evaluating the selector expression is
null
, and no switch label in the resolved switch block applies then aNullPointerException
is thrown and the entireswitch
statement completes abruptly for that reason. If applying completes abruptly, then the entireswitch
statement completes abruptly for the same reason.
- Otherwise, if the result of evaluating the selector expression is of type
Character
,Byte
,Short
, orInteger
, it is subjected to unboxing conversion (5.1.8). If this conversion completes abruptly, the entireswitch
statement completes abruptly for the same reason.
If evaluation of the selector expression completes normally and the result is non- then execution of the null
, and the subsequent unboxing conversion (if any) completes normally,switch
statement continues by determining if a switch label associated with in the resolved switch block matches applies to the value of the selector expression (14.11.1). Then:
If applying completes abruptly, then the entire
switch
statement completes abruptly for the same reason.If no switch label
matchesapplies then if theswitch
statement is an enhancedswitch
statement anIncompatibleClassChangeError
is thrown and the entireswitch
statement completes abruptly for that reason; otherwise, the entireswitch
statement completes normally.If a switch label
matchesapplies, then one of the followingappliesholds:If it is the switch label for a switch rule expression, then the switch rule expression is necessarily a statement expression (14.11.2). The statement expression is evaluated. If the evaluation completes normally, then the
switch
statement completes normally. If the result of evaluation is a value, it is discarded.If it is the switch label for a switch rule block, then the block is executed. If this block completes normally, then the
switch
statement completes normally.If it is the switch label for a switch rule
throw
statement, then thethrow
statement is executed.If it is the switch label for a switch labeled statement group, then all the statements in the switch block that follow the switch label are executed in order. If these statements complete normally, then the
switch
statement completes normally.Otherwise, there are no statements that follow the
matchedswitch label that applies in the switch block, and theswitch
statement completes normally.
If execution of any statement or expression in the switch block completes abruptly, it is handled as follows:
If execution of a statement completes abruptly because of a
break
with no label, then no further action is taken and theswitch
statement completes normally.Abrupt completion because of a
break
with a label is handled by the general rule for labeled statements (14.7).If execution of a statement or expression completes abruptly for any other reason, then the
switch
statement completes abruptly for the same reason.Abrupt completion because of a
yield
statement is handled by the general rule for switch expressions (15.28.2).
Example 14.11.3-1. Fall-Through in the switch
Statement
When a selector expression matches a switch label switch label applies, and that switch label is for a switch rule, the switch rule expression or statement introduced by the switch label is executed, and nothing else. In the case of a switch label for a statement group, all the block statements in the switch block that follow the switch label are executed, including those that appear after subsequent switch labels. The effect is that, as in C and C++, execution of statements can "fall through labels."
For example, the program:
class TooMany {
static void howMany(int k) {
switch (k) {
case 1: System.out.print("one ");
case 2: System.out.print("too ");
case 3: System.out.println("many");
}
}
public static void main(String[] args) {
howMany(3);
howMany(2);
howMany(1);
}
}
contains a switch
block in which the code for each case
falls through into the code for the next case
. As a result, the program prints:
many
too many
one too many
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.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
Primary patterns are the simplest forms of patterns, from which all others are constructed. A type pattern is used to test whether a value is an instance of the type appearing in the pattern. A parenthesized pattern is also treated syntactically as a primary pattern.
A guarded pattern consists of a contained primary pattern and a contained guarding expression and is used to test whether a value matches the pattern and additionally that the guarding boolean expression is true
.
- Pattern:
- TypePattern
- Pattern:
- PrimaryPattern
- GuardedPattern
- GuardedPattern:
- PrimaryPattern
&&
ConditionalAndExpression - PrimaryPattern:
- TypePattern
(
Pattern)
- TypePattern:
- LocalVariableDeclaration
The following productions from 4.3, 8.3, 8.4.1, and 14.4 are shown here for convenience:
- LocalVariableDeclaration:
- {VariableModifier} LocalVariableType VariableDeclaratorList
- VariableModifier:
- Annotation
final
- LocalVariableType:
- UnannType
var
- VariableDeclaratorList:
- VariableDeclarator {
,
VariableDeclarator}- VariableDeclarator:
- VariableDeclaratorId [
=
VariableInitializer]- VariableDeclaratorId:
- Identifier [Dims]
- Dims:
- {Annotation}
[
]
{{Annotation}[
]
}
See 8.3 for UnannType.
There is also a special any pattern, which is a pattern that arises from the process of resolving a pattern (14.30.2).
Currently, any patterns may not appear in a pattern
instanceof
expression, or in a pattern label of aswitch
expression orswitch
statement. It is possible that future versions of the Java programming language may relax this restriction.
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 LocalVariableType denotes a reference type (and furthermore is not
var
).The VariableDeclaratorList consists of a single VariableDeclarator.
The VariableDeclarator has no initializer.
The VariableDeclaratorId has no bracket pairs.
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.
A parenthesized pattern declares the local variables that are declared by the contained pattern.
The type of a parenthesized pattern is the type of the contained pattern.
An any pattern declares one local variable, known as a pattern variable.
The pattern variable of an any pattern has a type, which is a reference type.
The type of an any pattern is the type of its pattern variable.
A pattern variable is declared by a guarded pattern p&&e
if one of the following is true:
The pattern variable is declared by the pattern
p
The pattern variable is introduced by
e
when true (6.3.1).
It is a compile-time error if any pattern variable is both declared by the contained pattern and by the guarding expression of a guarded pattern when true.
Any variable that is used but not declared in the guarding expression of a guarded pattern must either be final
or effectively final (4.12.4).
The type of a guarded pattern is the type of the contained pattern.
An expression e is compatible with a pattern of type T if e is downcast compatible with T (5.5).
An expression e is compatible with a pattern if one of the following is true:
- The pattern is an any pattern; or
- The pattern is of type T and 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) and aswitch
expression orswitch
statement using patterns in a switch label (14.11.1).
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). If a value successfully matches a pattern, then the process of pattern matching initializes the pattern variable declared by the pattern.
Before pattern matching is performed, patterns are first resolved with respect to the type of the expression that they are to be matched against, resulting in a possibly amended pattern.
The rules for resolving a pattern at type U are as follows:
A guarded pattern, with contained pattern p, that is total for U, is resolved to the result of resolving p at type U. A guarded pattern, with contained pattern p and guarding expression e, that is not total for U is resolved to a guarded pattern where the contained pattern is the result of resolving p at type U, and the guarding expression is e.
A type pattern, p, declaring a pattern variable x of type T, that is total for U, is resolved to an any pattern that declares x of type T; otherwise it is resolved to p.
A parenthesized pattern, with contained pattern p, is resolved to a parenthesized pattern whose contained pattern is the result of resolving p at type U.
An any pattern, p, is resolved to p.
This process of resolving a pattern addresses a problem that arises when matching a null reference value against a type pattern. Checking whether a value matches a type pattern of type U involves checking (at runtime) whether the value can be cast to U. But the null reference can be cast to any reference type without any checks. If an expression of type V ultimately evaluates to the null reference, we'd still expect the match to only succeed if V was a subtype of the type of the type pattern.
This suggests that pattern matching needs to involve compile-time types. Rather than directly defining the runtime process of pattern matching with reference to compile-time types, we instead observe that pattern matching should satisfy the following property: the value of any expression whose static type is T always matches a type pattern of type S, where T is a subtype of S.
Thus, at compile-time we "resolve" the pattern with respect to the (compile-time) type of the expression being pattern matched. If it can be determined at compile-time that a pattern will always match, it is translated, or resolved, to a special any pattern (which, by definition, all values match without any examination of runtime types). This ensures the expected behavior when the value being matched is the null reference. In the other cases, resolving a pattern leaves the pattern untouched.
Some patterns contain expressions which are evaluated during pattern matching. Pattern matching is said to complete abruptly if evaluation of a contained expression completes abruptly. An abrupt completion always has an associated reason, which is always a throw
with a given value. Pattern matching is said to complete normally if it does not complete abruptly.
The rules for determining whether a value matches a pattern, and for initializing pattern variables, are as follows:
A value v (including the null reference) matches an any pattern.
The pattern variable declared by the any pattern is initialized to v.
The null reference does not match a type pattern.
In this case, the pattern variable declared by the type pattern is not initialized.
A value v that is not the null reference matches a type pattern of type T if v can be cast to T without raising a
ClassCastException
; and does not match otherwise.If v matches, then the pattern variable declared by the type pattern is initialized to v.
If v does not match, then the pattern variable declared by the type pattern is not initialized.
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.
A value matches a guarded pattern if (i) it matches the contained pattern, and (ii) the guarding expression evaluates to
true
; and does not match otherwise, provided all steps complete normally. If pattern matching the value with the contained pattern completes abruptly, or the value does not match the contained pattern, then no attempt is made to evaluate the guarding expression. If either step completes abruptly then pattern matching completes abruptly for the same reason.Note that the process of a value matching the contained pattern may initialize pattern variables that can be used in the guarding expression, e.g.
if (o instanceof String s && s.length() > 2) { System.out.println("A string containing at least two characters"); }
- A value matches a parenthesized pattern if it matches the contained pattern; and does not match otherwise. If pattern matching the value with the contained pattern completes abruptly then pattern matching completes abruptly for the same reason.
14.30.3 Pattern Totality and Dominance
A pattern is said to be total for a type T as follows:
A guarded pattern is total for T if (i) the contained pattern is total for T and (ii) the guarding expression is a constant expression whose value is
true
.A type pattern of type S is total for T if the erasure of T is a subtype of the erasure of S.
A parenthesized pattern is total for T if the contained pattern is total for T.
An any pattern is total for any T.
A pattern p is said to dominate a pattern q if every value that matches q also matches p, and is defined as follows:
A pattern p dominates a type pattern of type T if p is total for T.
A pattern p dominates a guarded pattern with contained pattern q if p dominates q.
A pattern p dominates a parenthesized pattern with contained pattern q if p dominates q.
A pattern p dominates an any pattern of type T is p is total for T.
Chapter 15: Expressions
15.20 Relational Operators
15.20.2 The instanceof
Operator
An instanceof
expression may perform either type comparison or pattern matching.
- InstanceofExpression:
- RelationalExpression
instanceof
ReferenceType - RelationalExpression
instanceof
PatternPrimaryPattern
If the operand to the right of the instanceof
keyword is a ReferenceType, then the instanceof
keyword is the type comparison operator.
If the operand to the right of the instanceof
keyword is a Pattern PrimaryPattern, then the instanceof
keyword is the pattern match operator.
The following rules apply when instanceof
is the type comparison operator:
The type of the expression RelationalExpression must be a reference type or the null type, or a compile-time error occurs.
The RelationalExpression must be downcast compatible with the ReferenceType (5.5), or a compile-time error occurs.
At run time, the result of the type comparison operator is determined as follows:
If the value of the RelationalExpression is the null reference (4.1), then the result is
false
.If the value of the RelationalExpression is not the null reference, then the result is
true
if the value could be cast to the ReferenceType without raising aClassCastException
, andfalse
otherwise.
The following rules apply when instanceof
is the pattern match operator:
The type of the expression RelationalExpression must be a reference type or the null type, or a compile-time error occurs.
The RelationalExpression must be compatible with the
PatternPrimaryPattern (14.30.1), or a compile-time error occurs.If the type of the RelationalExpression is a subtype of the type of the Pattern, then a compile-time error occurs.If the PrimaryPattern is total for the type of the expression RelationalExpression (14.30.3), then a compile-time error occurs.
This requirement eliminates pointless pattern matching
instanceof
expressions:String s = ... if (s instanceof Object o) // Compile-time error! System.out.println("Everything is an object!");
At run time, the result of the pattern match operator is determined as follows (where p is the pattern resulting from resolving the Pattern with the type of the RelationalExpression (14.30.2)):
If the value of the RelationalExpression is the null reference, then the result is
false
.If the value of the RelationalExpression is not the null reference, then the result is
true
if the value matchesthe Patternthe resolved pattern p (14.30.2), andfalse
otherwise. If pattern matching the value with the resolved pattern p completes abruptly, then theinstanceof
expression completes abruptly for the same reason.A side effect of a
true
result is that pattern variables declared inPatternthe resolved pattern p will be initialized.
Example 15.20.2-1. The Type Comparison Operator
class Point { int x, y; }
class Element { int atomicNumber; }
class Test {
public static void main(String[] args) {
Point p = new Point();
Element e = new Element();
if (e instanceof Point) { // compile-time error
System.out.println("I get your point!");
p = (Point)e; // compile-time error
}
}
}
This program results in two compile-time errors. The cast (Point)e
is incorrect because no instance of Element
or any of its possible subclasses (none are shown here) could possibly be an instance of any subclass of Point
. The instanceof
expression is incorrect for exactly the same reason. If, on the other hand, the class Point
were a subclass of Element
(an admittedly strange notion in this example):
class Point extends Element { int x, y; }
then the cast would be possible, though it would require a run-time check, and the instanceof
expression would then be sensible and valid. The cast (Point)e
would never raise an exception because it would not be executed if the value of e
could not correctly be cast to type Point
.
Prior to Java SE 16, the ReferenceType operand of a type comparison operator was required to be reifiable (4.7). This prevented the use of a parameterized type unless all its type arguments were wildcards. The requirement was lifted in Java SE 16 to allow more parameterized types to be used. For example, in the following program, it is legal to test whether the method parameter x
, with static type List<Integer>
, has a more "refined" parameterized type ArrayList<Integer>
at run time:
import java.util.ArrayList;
import java.util.List;
class Test2 {
public static void main(String[] args) {
List<Integer> x = new ArrayList<Integer>();
if (x instanceof ArrayList<Integer>) { // OK
System.out.println("ArrayList of Integers");
}
if (x instanceof ArrayList<String>) { // error
System.out.println("ArrayList of Strings");
}
if (x instanceof ArrayList<Object>) { // error
System.out.println("ArrayList of Objects");
}
}
}
The first instanceof
expression is legal because there is a casting conversion from List<Integer>
to ArrayList<Integer>
. However, the second and third instanceof
expressions both cause a compile-time error because there is no casting conversion from List<Integer>
to ArrayList<String>
or ArrayList<Object>
.
15.28 switch
Expressions
A switch
expression transfers control to one of several statements or expressions, depending on the value of an expression; all possible values of that expression must be handled, and all of the several statements and expressions must produce a value for the result of the switch
expression.
- SwitchExpression:
switch
(
Expression)
SwitchBlock
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.
The body of both a
switch
expression and aswitch
statement (14.11) is called a switch block. General rules which apply to all switch blocks, whether they appear inswitch
expressions orswitch
statements, are given in 14.11.1. The following productions from 14.11.1 are shown here for convenience:
- SwitchBlock:
{
SwitchRule {SwitchRule}}
{
{SwitchBlockStatementGroup} {SwitchLabel:
}}
- SwitchRule:
- SwitchLabel
->
Expression;
- SwitchLabel
->
Block- SwitchLabel
->
ThrowStatement- SwitchBlockStatementGroup:
- SwitchLabel
:
{SwitchLabelBlockStatements:
}
- SwitchLabel:
case
CaseConstant {,
CaseConstant}default
- SwitchLabel:
- CaseOrDefaultLabel {
:
CaseOrDefaultLabel }- CaseOrDefaultLabel:
case
CaseLabelElement {,
CaseLabelElement }default
- CaseLabelElement:
- CaseConstant
- Pattern
null
default
- CaseConstant:
- ConditionalExpression
15.28.1 The Switch Block of a switch
Expression
In addition to the general rules for switch blocks (14.11.1), there are further rules for switch blocks in switch
expressions. Namely, all of the following must be true for the switch block of a switch
expression, or a compile-time error occurs:
If the type of the selector expression is not an enum type, then there is exactly one
default
label associated with the switch block.If the type of the selector expression is an enum type, then (i) the set of the
case
constants associated with the switch block includes every enum constant of the enum type, and (ii) at most onedefault
label is associated with the switch block.A
default
label is permitted, but not required, when thecase
labels cover all the enum constants.
If the switch block consists of switch rules, then any switch rule block cannot complete normally (14.22).
If the switch block consists of switch labeled statement groups, then the last statement in the switch block cannot complete normally, and the switch block does not have any switch labels after the last switch labeled statement group.
switch
expressions cannot have empty switch blocks, unlikeswitch
statements. Furthermore,switch
expressions differ fromswitch
statements 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 aswitch
expression, any expression may be used as a switch rule expression, but in aswitch
statement, only a statement expression may be used (14.11.1).
A switch
expression must be exhaustive (14.11.1), or a compile-time error occurs.
The result expressions of a switch
expression are determined as follows:
If the switch block consists of switch rules, then each switch rule is considered in turn:
If the switch rule is of the form
...
->
Expression then Expression is a result expression of theswitch
expression.If the switch rule is of the form
...
->
Block then every expression which is immediately contained in ayield
statement in Block whose yield target is the givenswitch
expression, is a result expression of theswitch
expression.
If the switch block consists of switch labeled statement groups, then every expression immediately contained in a
yield
statement in the switch block whose yield target is the givenswitch
expression, is a result expression of theswitch
expression.
It is a compile-time error if a switch
expression has no result expressions.
A switch
expression is a poly expression if it appears in an assignment context or an invocation context (5.2, 5.3). Otherwise, it is a standalone expression.
Where a poly switch
expression appears in a context of a particular kind with target type T, its result expressions similarly appear in a context of the same kind with target type T.
A poly switch
expression is compatible with a target type T if each of its result expressions is compatible with T.
The type of a poly switch
expression is the same as its target type.
The type of a standalone switch
expression is determined as follows:
If the result expressions all have the same type (which may be the null type (4.1)), then that is the type of the
switch
expression.Otherwise, if the type of each result expression is
boolean
orBoolean
, then an unboxing conversion (5.1.8) is applied to each result expression of typeBoolean
, and theswitch
expression has typeboolean
.Otherwise, if the type of each result expression is convertible to a numeric type (5.1.8), then the type of the
switch
expression is the result of general numeric promotion (5.6) applied to the result expressions.Otherwise, boxing conversion (5.1.7) is applied to each result expression that has a primitive type, after which the type of the
switch
expression is the result of applying capture conversion (5.1.10) to the least upper bound (4.10.4) of the types of the result expressions.
15.28.2 Run-Time Evaluation of switch
Expressions
Evaluation of a switch
expression is always with respect to a resolved switch block (14.11.1). The switch block is resolved at type T, where T is the type of the selector expression.
A switch
expression is evaluated by first evaluating the selector expression. Then:
If evaluation of the selector expression completes abruptly, then the
switch
expression completes abruptly for the same reason.Otherwise, if the result of evaluating the selector expression is
null
, and no switch label in the resolved switch block applies then aNullPointerException
is thrown and the entireswitch
expression completes abruptly for that reason. If applying completes abruptly, then the entireswitch
expression completes abruptly for the same reason.
- Otherwise, if the result of evaluating the selector expression is non-
null
, and of typeCharacter
,Byte
,Short
, orInteger
, it is subjected to unboxing conversion (5.1.8). If this conversion completes abruptly, then the entireswitch
expression completes abruptly for the same reason.
If evaluation of the selector expression completes normally and the result is non- then evaluation of the null
, and the subsequent unboxing conversion (if any) completes normally,switch
expression continues by determining if a switch label associated with in the resolved switch block matches applies to the value of the selector expression (14.11.1). Then:
If applying completes abruptly, then the entire
switch
expression completes abruptly for the same reason.If no switch label
matchesapplies, then anIncompatibleClassChangeError
is thrown and the entireswitch
expression completes abruptly for that reason.If a switch label
matchesapplies, then one of the followingappliesholds:If it is the switch label for a switch rule expression, then the expression is evaluated. If the result of evaluation is a value, then the
switch
expression completes normally with the same value.If it is the switch label for a switch rule block, then the block is executed. If this block completes normally, then the
switch
expression completes normally.If it is the switch label for a switch rule
throw
statement, then thethrow
statement is executed.Otherwise, all the statements in the switch block after the
matchingswitch label that applies are executed in order. If these statements complete normally, then theswitch
expression completes normally.
If execution of any statement or expression in the switch block completes abruptly, it is handled as follows:
If execution of an expression completes abruptly, then the
switch
expression completes abruptly for the same reason.If execution of a statement completes abruptly because of a
yield
with value V, then theswitch
expression completes normally and the value of theswitch
expression is V.If execution of a statement completes abruptly for any reason other than a
yield
with a value, then theswitch
expression completes abruptly for the same reason.
Chapter 16: Definite Assignment
16.2 Definite Assignment and Statements
16.2.9 switch
Statements
V is [un]assigned after a
switch
statement (14.11) iff all of the following are true:V is [un]assigned before every
break
statement (14.15) that may exit theswitch
statement.For each switch rule (14.11.1) in the switch block, V is [un]assigned after the switch rule expression, switch rule block, or switch rule
throw
statement introduced by the switch rule.If there is a switch labeled statement group in the switch block, then V is [un]assigned after the last block statement of the last switch labeled statement group.
If
there is nothe switch block covers the type of the selector expression, or if the switch block ends with a switch label followed by thedefault
label in the switch block}
separator, then V is [un]assigned after the selector expression.
V is [un]assigned before the selector expression of a
switch
statement iff V is [un]assigned before theswitch
statement.V is [un]assigned before the switch rule expression, switch rule block, or switch rule
throw
statement introduced by a switch rule in the switch block iff V is [un]assigned after the selector expression of theswitch
statement.V is [un]assigned before the first block statement of a switch labeled statement group in the switch block iff both of the following are true:
V is [un]assigned after the selector expression of the
switch
statement.If the switch labeled statement group is not the first in the switch block, V is [un]assigned after the last block statement of the preceding switch labeled statement group.
V is [un]assigned before a block statement that is not the first of a switch labeled statement group in the switch block iff V is [un]assigned after the preceding block statement.