This document describes changes to the Java Language Specification to support Pattern Matching for switch, a preview feature of Java SE 17. See JEP 406 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.
JEP 409 proposes making sealed classes a final feature in Java SE 17. This impacts on conditions imposed on switch blocks of switch statements and expressions. The details accounting for sealed classes are included here for completeness.
Changelog:
2021-06-08:
Fixed errors in the definition of a switch block covering a type.
Renamed "complete" to "exhaustive".
2021-05-27:
Clarified restriction on
instanceofpatternsAdded missing restriction that a switch label can not have both a constant case label and a pattern case label, e.g.
case "42", String s.Added missing rule that a total pattern switch label dominates a null switch label
2021-05-14:
Added missing details of pattern matching completing abruptly.
Added a new "any" pattern (which is currently not denotable in the language, but is used in the specification)
Added new concept of a pattern resolving at a type to capture the semantics of type patterns wrt null values (this removes requirement to make the runtime notion of pattern matching dependent on a compile-time type). Patterns in switch labels are also resolved, resulting in a resolved switch block. In other words, evaluation of a pattern
instanceofis always wrt a resolved pattern. Execution of aswitchstatement orswitchexpression is also always wrt to a resolved switch block.A pattern case label element that is total for the type of the selector expression will now match all values including
null.A
switchstatement that uses the new features of this JEP is now required to be exhaustive (the same notion that allswitchexpressions require)
2021-04-30:
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
throwstatement.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
throwstatement.
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
throwstatement.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
throwstatement.
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
pis definitely matched ine.It is a compile-time error if a pattern variable declared by
pis 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 14: Blocks and 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.
- 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:
caseCaseConstant {,CaseConstant}default
- SwitchLabel:
- CaseOrDefaultLabel {
:CaseOrDefaultLabel } - CaseOrDefaultLabel:
caseCaseLabelElement {,CaseLabelElement }default- CaseLabelElement:
- CaseConstant
- Pattern
nulldefault
- 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 rulethrowstatement; 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
defaultcase label element, or thenullcase label element.A switch label may not use more than one
defaultlabel.A switch label may not have more than one
defaultcase label element.A switch label may not have both a
defaultcase label element and use adefaultlabel.A switch label may not have more than one
nullcase label element.If a switch label has a
nullcase 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 a pattern case label element where the pattern is an any pattern.
Any patterns may only appear in switch labels as the result of resolving the switch block.
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
defaultlabel.A switch label may not have both a pattern case label element and a
defaultcase 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 2andcase 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> lsdominates the type patternList<Integer> liand 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
switchstatement orswitchexpression dominates a switch label that has anullcase 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 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.
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 icould be reached from the preceding statement group, and so the pattern variableiwill 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).caseconstant associated with the switch blockIf the type of e, T, is an enum type, then c
everyis an enum constant of type T.caseconstant associated with the switch block
A
nullcase label element is compatible with e if the type of e is a reference type.A
defaultcase 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.
The following definitions assume the existence of sealed classes, a feature proposed by JEP 409 to be finalized in Java SE 17.
A type T supports a sealed class or interface C if and only if one of the following holds:
T is a class type that names C, and the class C is both
sealedandabstract.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).
A switch block covers a 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
sealedclass or interface C, and the switch block covers all of the permitted direct subclasses and subinterfaces of C.Note that a default switch label is permitted, but not required in the case where all the permited direct subclasses and subinterfaces of a
sealedclass or interface are covered by the switch block. 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 testSealedCoverage(I i) { return switch(i) { case A a -> 0; case B b -> 1; case C c -> 2; // No default required! }; }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 the switch block covers 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).
A switch block is resolved at type U resulting in a identical switch block except where every pattern case label element p is replaced with a pattern case label element whose pattern is the result of resolving pattern p at type U.
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
nullcase label element applies. In addition, any pattern variables declared by patterns appearing in the switch label are initialized to the null reference.A switch label that has a pattern case label element p where p is an any pattern applies. In addition, the pattern variable declared by the any pattern is initialized to the null reference.
It is possible for a single resolved switch label to have both a
nullcase label and an any pattern. In this case, both rules are identical and either one can be used.
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.caseconstants is equal to the value, then we say that thecaselabel which contains thecaseconstant 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 theequalsmethod of classString.A switch label that has a pattern case label element p applies if the value matches the pattern p. If pattern matching completes abruptly then applying completes abruptly for the same reason.
If no switch
label that is not a default switch labelcasematchesapplies
but there is adefault switch label, then thedefaultdefault switch label applies.defaultA switch block may have at most one default switch label.
A
switch label cancasecontainhave severalconstant case label elements. The labelcaseconstantsmatchesapplies 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 variablecasedayis either one of the enum constants shown:switch (day) { ... case SATURDAY, SUNDAY : System.out.println("It's the weekend!"); break; ... }
nullcannot be used as acaseconstant because it is not a constant expression. Even ifcasenullwas allowed, it would be undesirable because the code in thatcasecan never be executed. Namely, if the selector expression is of a reference type (that is,Stringor a boxed primitive type or an enum type), then an exception will occur if the selector expression evaluates tonullat run time. In the judgment of the designers of the Java programming language, propagating the exception is a better outcome than either having nocaselabel match, or having thedefaultlabel match.
In C and C++ the body of a
switchstatement can be a statement and statements withcaselabels do not have to be immediately contained by that statement. Consider the simple loop:for (i = 0; i < n; ++i) foo();where
nis known to be positive. A trick known as Duff's device can be used in C or C++ to unroll the loop, but this is not valid code in the Java programming language:int q = (n+7)/8; switch (n%8) { case 0: do { foo(); // Great C hack, Tom, case 7: foo(); // but it's not valid here. case 6: foo(); case 5: foo(); case 4: foo(); case 3: foo(); case 2: foo(); case 1: foo(); } while (--q > 0); }Fortunately, this trick does not seem to be widely known or used. Moreover, it is less needed nowadays; this sort of code transformation is properly in the province of state-of-the-art optimizing compilers.
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 onedefaultlabel is associated with theswitchblock.Every switch rule expression in the switch block is a statement expression (14.8).
switchstatements differ fromswitchexpressions 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 aswitchstatement, only a statement expression may be used as a switch rule expression, but in aswitchexpression, any expression may be used (15.28.1).If the
switchstatement is an enhancedswitchstatement, then it must be exhaustive.
Prior to Java SE 17,
switchstatements (andswitchexpressions) 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, unlikeswitchexpressions,switchstatements did not have to be exhaustive. This is often the cause of difficult to detect bugs, where no switch label applies and theswitchstatement 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,
switchstatements have been enhanced in the sense that the two limitations listed above have been lifted. The designers of the Java programming language decided that enhancedswitchstatements should align withswitchexpressions and be required to be exhaustive. This is often achieved with the addition of a trivial default switch label. For example, the following enhancedswitchstatement 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,
switchstatements that are not an enhancedswitchstatement are not required to be exhaustive.
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
switchstatement 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 aNullPointerExceptionis thrown and the entireswitchstatement completes abruptly for that reason. If applying completes abruptly, then the entireswitchstatement 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 entireswitchstatement 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
switchstatement completes abruptly for the same reason.If no switch label
matchesapplies then if theswitchstatement is an enhancedswitchstatement anIncompatibleClassChangeErroris thrown and the entireswitchstatement completes abruptly for that reason; otherwise, the entireswitchstatement 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
switchstatement 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
switchstatement completes normally.If it is the switch label for a switch rule
throwstatement, then thethrowstatement 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
switchstatement completes normally.Otherwise, there are no statements that follow the
matchedswitch label that applies in the switch block, and theswitchstatement 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
breakwith no label, then no further action is taken and theswitchstatement completes normally.Abrupt completion because of a
breakwith 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
switchstatement completes abruptly for the same reason.Abrupt completion because of a
yieldstatement 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
instanceofexpression, or in a pattern label of aswitchexpression orswitchstatement. 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
pThe pattern variable is introduced by
ewhen 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
instanceofpattern match operator (15.20.2) and aswitchexpression orswitchstatement 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).
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
instanceofpattern 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
instanceofReferenceType - RelationalExpression
instanceofPatternPrimaryPattern
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, 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
trueif the value could be cast to the ReferenceType without raising aClassCastException, andfalseotherwise.
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 Pattern (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 Pattern is total for the type of the expression RelationalExpression (14.30.3), then a compile-time error occurs.
This requirement eliminates pointless pattern matching
instanceofexpressions:String s = ... if (s instanceof Object o) // Compile-time error! System.out.println("Everything is an object!");It additionally means that the pattern in an
instanceofexpression, can not be an any pattern:String s = ... if (s instanceof var obj) // Compile-time error! System.out.println("Pointless!");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
trueif the value matchesthe Patternthe resolved pattern p (14.30.2), andfalseotherwise. If pattern matching the value with the resolved pattern p completes abruptly, then theinstanceofexpression completes abruptly for the same reason.A side effect of a
trueresult 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
switchexpression and aswitchstatement (14.11) is called a switch block. General rules which apply to all switch blocks, whether they appear inswitchexpressions orswitchstatements, 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:
caseCaseConstant {,CaseConstant}default
- SwitchLabel:
- CaseOrDefaultLabel {
:CaseOrDefaultLabel }- CaseOrDefaultLabel:
caseCaseLabelElement {,CaseLabelElement }default- CaseLabelElement:
- CaseConstant
- Pattern
nulldefault
- 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
defaultlabel associated with the switch block.If the type of the selector expression is an enum type, then (i) the set of the
caseconstants associated with the switch block includes every enum constant of the enum type, and (ii) at most onedefaultlabel is associated with the switch block.A
defaultlabel is permitted, but not required, when thecaselabels 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.
switchexpressions cannot have empty switch blocks, unlikeswitchstatements. Furthermore,switchexpressions differ fromswitchstatements 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 aswitchexpression, any expression may be used as a switch rule expression, but in aswitchstatement, 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 theswitchexpression.If the switch rule is of the form
...->Block then every expression which is immediately contained in ayieldstatement in Block whose yield target is the givenswitchexpression, is a result expression of theswitchexpression.
If the switch block consists of switch labeled statement groups, then every expression immediately contained in a
yieldstatement in the switch block whose yield target is the givenswitchexpression, is a result expression of theswitchexpression.
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
switchexpression.Otherwise, if the type of each result expression is
booleanorBoolean, then an unboxing conversion (5.1.8) is applied to each result expression of typeBoolean, and theswitchexpression has typeboolean.Otherwise, if the type of each result expression is convertible to a numeric type (5.1.8), then the type of the
switchexpression 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
switchexpression 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
switchexpression 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 aNullPointerExceptionis thrown and the entireswitchexpression completes abruptly for that reason. If applying completes abruptly, then the entireswitchexpression 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 entireswitchexpression 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
switchexpression completes abruptly for the same reason.If no switch label
matchesapplies, then anIncompatibleClassChangeErroris thrown and the entireswitchexpression 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
switchexpression 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
switchexpression completes normally.If it is the switch label for a switch rule
throwstatement, then thethrowstatement 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 theswitchexpression 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
switchexpression completes abruptly for the same reason.If execution of a statement completes abruptly because of a
yieldwith value V, then theswitchexpression completes normally and the value of theswitchexpression is V.If execution of a statement completes abruptly for any reason other than a
yieldwith a value, then theswitchexpression 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
switchstatement (14.11) iff all of the following are true:V is [un]assigned before every
breakstatement (14.15) that may exit theswitchstatement.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
throwstatement 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 thedefaultlabel in the switch block}separator, then V is [un]assigned after the selector expression.
V is [un]assigned before the selector expression of a
switchstatement iff V is [un]assigned before theswitchstatement.V is [un]assigned before the switch rule expression, switch rule block, or switch rule
throwstatement introduced by a switch rule in the switch block iff V is [un]assigned after the selector expression of theswitchstatement.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
switchstatement.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.