2.2 Switch Statements and Switch Expressions
Key Takeaways
- Switch selector types are restricted to char, byte, short, int, Character, Byte, Short, Integer, String, and enum types (excluding long, float, double, and boolean).
- Traditional switch statements use colon syntax (case X:) and require explicit break statements to avoid fall-through execution.
- Switch expressions (standardized in Java 14) utilize arrow syntax (case X ->) to eliminate fall-through and mandate exhaustive coverage of all possible selector values.
- The contextual keyword yield returns a value from a multi-statement block inside a switch expression, whereas return exits the enclosing method.
Switch Statements, Switch Expressions, and Yield
The switch construct in Java has evolved from a legacy C-style branching statement into a versatile, expression-oriented construct. The 1Z0-830 exam tests both classic switch statements and modern switch expressions, including selector data types, fall-through mechanics, constant expressions, the yield keyword, and compiler exhaustiveness checks.
1. Supported Switch Selector Data Types
The type of the switch selector expression determines which features and case labels are valid:
Classic Switch Selector Types (Java 1.0 - Java 20):
- Integral Primitives:
byte,short,char,int. - Wrapper Classes:
Byte,Short,Character,Integer. - Strings:
java.lang.String(supported since Java 7). - Enumerations:
enumconstants (supported since Java 5).
Unsupported Selector Types in Non-Pattern Switch:
long,float,double,boolean(and their respective wrappersLong,Float,Double,Boolean). Attempting to switch over alongordoublewithout pattern matching yields a compile-time error:incompatible types: possible lossy conversion from long to int.
Pattern Matching Switch Selector Types (Java 21):
- In Java 21, pattern matching allows any reference type (including
Object, interfaces, and custom record classes) as the selector expression.
2. Classic switch Statements (: Colon Syntax)
A classic switch statement uses case labels ending with a colon (:).
Fall-Through Behavior
Execution enters at the first matching case label and continues sequentially through subsequent case blocks and default until encountering a break, return, throw, or the end of the switch block:
int day = 2;
switch (day) {
case 1:
System.out.print("Mon ");
case 2:
System.out.print("Tue "); // Matches day = 2
case 3:
System.out.print("Wed "); // Falls through!
break;
case 4:
System.out.print("Thu ");
default:
System.out.print("Weekend ");
}
// Output: "Tue Wed "
Constant Expression Requirements for Case Labels
Every case value in a classic switch must be a compile-time constant expression assignable to the selector type:
- Literals (e.g.,
case 1:,case "ACTIVE":) finalvariables initialized with compile-time constant expressions (e.g.,final int MAX = 10; case MAX:)- Enum constants (written without the qualifying enum type name:
case ACTIVE:rather thancase Status.ACTIVE:)
final int x = 10;
int y = 20; // non-final
final int z;
z = 30; // blank final, NOT a compile-time constant!
int target = 10;
switch (target) {
case x: // Valid: compile-time constant
break;
// case y: // COMPILE ERROR: constant expression required
// break;
// case z: // COMPILE ERROR: constant expression required
// break;
}
Single Shared Lexical Scope
In colon-syntax switch statements, the entire switch block forms a single lexical scope. Local variables declared in one case are in scope for all subsequent cases, which can lead to variable declaration clashes:
int mode = 1;
switch (mode) {
case 1:
int count = 10; // Declared in switch scope
System.out.println(count);
break;
case 2:
// int count = 20; // COMPILE ERROR: Variable 'count' already defined in scope
count = 20; // Legal: reassigning the variable declared in case 1!
System.out.println(count);
break;
}
To isolate local variables within a single case, the case body must be wrapped in explicit block braces: case 1: { int count = 10; break; }.
3. Modern switch Expressions (-> Arrow Syntax)
Introduced in Java 14 (JEP 361), switch expressions treat branching as a value-producing expression.
Arrow (->) Syntax Rules
- No Fall-Through: Only the expression, block, or throw statement to the right of the arrow is executed. No
breakstatements are needed or allowed to prevent fall-through. - Multiple Comma-Separated Labels: Multiple values can be combined into a single case label:
case 1, 2, 3 -> "Q1";. - Right-Hand Side Forms:
- Single expression:
case 1 -> "One"; - Block with
yield:case 2 -> { String res = "Two"; yield res; } - Exception throw:
case 3 -> throw new IllegalArgumentException();
- Single expression:
String season = switch (month) {
case 12, 1, 2 -> "Winter";
case 3, 4, 5 -> "Spring";
case 6, 7, 8 -> "Summer";
case 9, 10, 11 -> "Autumn";
default -> "Invalid month";
}; // Semicolon required when used in assignment!
4. The yield Keyword and Scope Isolation
The yield Statement
yield is a contextual keyword (not a reserved identifier) introduced to return values from switch expression blocks:
int score = 85;
String grade = switch (score / 10) {
case 10, 9 -> "A";
case 8 -> {
System.out.println("Good job!");
yield "B"; // Returns "B" as the switch expression result
}
case 7 -> "C";
default -> "F";
};
yield vs. return vs. break
yield value;: Produces a value for a switch expression block. It cannot be used in a switch statement.return value;: Exits the enclosing method, NOT just the switch expression. Attempting to return a switch value withreturninside a switch expression block will exit the method entirely!break;: Valid in classic colon switch statements to terminate execution. Usingbreak value;is obsolete syntax from early preview versions and will not compile.
Lexical Isolation with Arrow Syntax
Unlike colon-syntax switch statements, each arrow branch with curly braces {} defines its own independent local variable scope:
int val = 1;
switch (val) {
case 1 -> {
int temp = 100;
System.out.println(temp);
}
case 2 -> {
int temp = 200; // Perfectly legal! Isolated scope.
System.out.println(temp);
}
default -> {}
}
5. Switch Statement vs. Switch Expression Comparison
| Feature | Classic Switch Statement | Modern Switch Expression |
|---|---|---|
| Syntax | Case labels with colon : | Arrow -> or Colon : with yield |
| Produces a Value? | No (Statement) | Yes (Expression) |
| Trailing Semicolon | No (switch (x) { ... }) | Yes when assigned (var res = switch(x) { ... };) |
| Fall-Through | Yes by default (requires break) | No fall-through with -> arrow syntax |
| Exhaustiveness | Not required (unless pattern switch) | Strictly required by compiler |
| Multiple Labels | Stacked case 1: case 2: | Comma-separated case 1, 2 -> |
| Value Return Mechanism | N/A | -> expr or yield value; |
| Mixed Syntax | Illegal to mix -> and : in the same switch block | Illegal to mix -> and : in the same switch block |
6. Compiler Exhaustiveness Requirements
When switch is used as an expression, the Java compiler requires that it handles all possible values of the selector type:
- For primitive types (
int,char, etc.) andString, adefaultcase is almost always mandatory because their domain of values is effectively infinite. - For
enumtypes, if all enum constants are covered, the compiler does not require adefaultclause. However, if any enum constant is missing and there is nodefault, compilation fails withthe switch expression does not cover all possible input values.
enum Status { PENDING, APPROVED, REJECTED }
Status s = Status.PENDING;
// Compiles cleanly without default because all 3 enum values are handled:
String text = switch (s) {
case PENDING -> "Please wait";
case APPROVED -> "Welcome";
case REJECTED -> "Access denied";
};
What is the printed output of the following Java program?
int rating = 2;
switch (rating) {
default:
System.out.print("Def ");
case 1:
System.out.print("One ");
break;
case 2:
System.out.print("Two ");
case 3:
System.out.print("Three ");
}
Which of the following statements regarding switch expressions in Java 21 is correct?
Given the following code, what is the compilation result?
final int a = 10;
int b = 20;
final int c;
c = 30;
int test = 10;
switch (test) {
case a -> System.out.println("A");
case b -> System.out.println("B");
case c -> System.out.println("C");
}
What is the compilation result of the following code snippet?
int mode = 1;
switch (mode) {
case 1:
int size = 10;
System.out.print(size);
break;
case 2:
int size = 20;
System.out.print(size);
break;
default:
System.out.print(0);
}