4.2 switch Statements and Expressions
Prerequisites: Conditional branches and Java 17 basic syntax Estimated time: 30 minutes You are ready to continue when: You can distinguish traditional colon branches, arrow branches, and switch expressions
Traditional switch Statements
String item = "book";
switch (item) {
case "book":
System.out.println("50 credits");
break;
case "magazine":
System.out.println("40 credits");
break;
case "notebook":
System.out.println("35 credits");
break;
default:
System.out.println("unknown item");
}This page uses final Java 17 syntax without preview features. A switch selector supports byte, short, char, and int, as well as their corresponding wrapper types, enums, and String. It does not support long, float, double, or boolean. A null wrapper, enum, or String selector throws NullPointerException; arrow labels and default do not catch it. Handle null before entering the switch.
Colon Branches Can Fall Through
Execution starts at the matching case: label. If it does not encounter break, return, throw, or the end of switch, it continues executing the following statements:
int day = 6;
switch (day) {
case 6:
System.out.println("Saturday");
case 7:
System.out.println("weekend");
break;
default:
System.out.println("weekday");
}When day is 6, it prints two lines. This could be intentionally shared logic, or it could be a missed break. Comment intentional fall-through and test the resulting behavior.
Sharing multiple labels does not require copying empty branches:
switch (day) {
case 1:
case 2:
case 3:
case 4:
case 5:
System.out.println("weekday");
break;
case 6:
case 7:
System.out.println("weekend");
break;
default:
throw new IllegalArgumentException("day: " + day);
}Arrow Branches Do Not Fall Through
Java 14 onwards officially provides arrow syntax, and Java 17 allows direct use:
switch (item) {
case "book" -> System.out.println("50 credits");
case "magazine" -> System.out.println("40 credits");
case "notebook" -> System.out.println("35 credits");
default -> System.out.println("unknown item");
}When a branch requires multiple statements, use a block:
case "book" -> {
recordSale(item);
System.out.println("50 credits");
}Multiple constants can be written under the same label:
case "book", "magazine" -> System.out.println("physical item");switch Expression Return Value
int price = switch (item) {
case "book" -> 50;
case "magazine" -> 40;
case "notebook" -> 35;
default -> throw new IllegalArgumentException("item: " + item);
};This is an expression, and the final declaration statement requires a semicolon. Each normal path must produce a compatible result; a path that abruptly ends with throw does not require a result value.
Block branches provide values through yield:
int price = switch (item) {
case "book" -> {
int base = 50;
System.out.println("featured item");
yield base;
}
case "magazine" -> 40;
default -> 0;
};yield terminates the current switch expression and provides a result, which is not equivalent to returning from the entire method via return.
Exhaustiveness and default
Switch expressions must satisfy the compiler’s exhaustiveness check; that does not make null a fallback case. For open sets of integers or strings, default is typically required. When enumerating all constants of an enum, the compiler can determine completeness. Whether to still include default depends on whether you want new enum constants to trigger a compilation failure when recompiled, or to explicitly handle them with a fallback.
Do not use default -> 0 to hide inputs that should be rejected. Scenarios like price, permissions, protocol states are usually better suited for throwing explicit exceptions or returning a type that can express the absence of a value.
When to Choose switch
- A discrete value mapped to a fixed branch: suitable for switch;
- Ranges, multiple independent conditions, or complex predicates:
if / else ifis more natural; - Branches that keep growing with each branch containing a lot of behavior: consider enum polymorphism, a mapping table, or a strategy object, rather than infinitely expanding switch.
Python and C++ Boundaries
Python 3.10+ has match as structured pattern matching, and its capabilities and semantics go beyond simply renaming Java switch. It does not have the traditional fall-through behavior found in C++ switch.
C++ switch typically uses integer or enumeration selectors, and colon branches can also fall through; it does not have Java's String switch, nor does it have the arrow syntax and yield semantics of Java 17's switch expression.
Professional Reference
| Requirement | Java 17 Choice |
|---|---|
| Execute only branch actions | switch statement |
| Compute a value | switch expression |
| Avoid fall-through | arrow labels |
| Multiple statements and return a value | arrow block + yield |
| Unknown value should fail | default -> throw ... |
| selector may be null | explicitly handle before switch |
Before Moving On
Assign a path to each of "book", the unknown item, and null. Then explain when a colon branch falls through, why an arrow branch doesn't need break, and why yield only passes the value to the switch expression rather than exiting the entire method. Finally, confirm that the selector is not long or boolean.
The next lesson extends one branch decision into repeated processing of ten delivery records.
→ Next: while, for, and traversal
Specification: JLS §14.11: switch Statements, §15.28: switch Expressions.