Contents
- Arrow-case Syntax
- Returning Values with yield
- Exhaustiveness
- Multiple Labels per Case
- Practical Examples
The arrow -> replaces the colon :. Arrow cases do not fall through, so break is never needed. Each arrow case can be a single expression, a block, or a throw.
// Old style — fall-through prone, verbose
int numLetters;
switch (day) {
case MONDAY:
case FRIDAY:
case SUNDAY:
numLetters = 6;
break;
case TUESDAY:
numLetters = 7;
break;
case THURSDAY:
case SATURDAY:
numLetters = 8;
break;
case WEDNESDAY:
numLetters = 9;
break;
default:
throw new IllegalArgumentException("Unknown day: " + day);
}
// New style — switch expression with arrow syntax
int numLetters = switch (day) {
case MONDAY, FRIDAY, SUNDAY -> 6;
case TUESDAY -> 7;
case THURSDAY, SATURDAY -> 8;
case WEDNESDAY -> 9;
};
// No default needed: Day is an enum — compiler checks exhaustiveness
Arrow cases never fall through. If you need fall-through behavior for legacy reasons, stick to colon-style cases — but prefer arrow syntax for all new code.
When a case body is a block (not a single expression), use yield to return the value from the block. yield is a context-sensitive keyword — it only has meaning inside a switch expression block.
int result = switch (input) {
case "one" -> 1;
case "two" -> 2;
case "three" -> 3;
default -> {
// block body — must use yield to produce the value
System.out.println("Unknown input: " + input);
yield -1;
}
};
// yield also works with colon-style cases (traditional syntax)
int score = switch (grade) {
case 'A':
System.out.println("Excellent!");
yield 100;
case 'B':
yield 85;
case 'C':
yield 70;
default:
yield 0;
};
yield is only valid inside a switch expression — not a switch statement. A switch expression MUST yield a value from every execution path (the compiler enforces this).
A switch expression (one that produces a value) must cover all possible values. For enums, the compiler checks that every constant is handled. For other types, a default or case null, default is required.
enum Season { SPRING, SUMMER, AUTUMN, WINTER }
// Exhaustive — all 4 enum constants are covered, no default needed
String activity = switch (season) {
case SPRING -> "Gardening";
case SUMMER -> "Swimming";
case AUTUMN -> "Hiking";
case WINTER -> "Skiing";
};
// If you add a new enum constant (e.g., MONSOON) without updating
// the switch, it becomes a compile-time error — not a silent runtime bug.
// For String/int selectors — default is required
String category = switch (statusCode) {
case 200, 201 -> "Success";
case 400 -> "Bad Request";
case 401, 403 -> "Auth Error";
case 404 -> "Not Found";
case 500 -> "Server Error";
default -> "Unknown (" + statusCode + ")";
};
Multiple case labels can be combined on a single case using commas, both in arrow and traditional syntax:
// Multiple constants → same result
String type = switch (ch) {
case 'a', 'e', 'i', 'o', 'u',
'A', 'E', 'I', 'O', 'U' -> "vowel";
default -> "consonant";
};
// Switch expression used directly as a method argument
System.out.println(switch (month) {
case JANUARY, MARCH, MAY, JULY, AUGUST, OCTOBER, DECEMBER -> 31;
case APRIL, JUNE, SEPTEMBER, NOVEMBER -> 30;
case FEBRUARY -> year % 4 == 0 ? 29 : 28;
});
Switch expressions are most valuable for mapping a finite set of inputs to output values — enum constants to labels, status codes to categories, type tokens to behavior. The arrow form (case X -> result) is preferred for single-expression cases because it removes fall-through entirely and keeps each arm on one line. When a case requires multiple statements, use a block with yield to return the value. Combined with sealed classes, switch expressions become exhaustive type dispatch: the compiler verifies every subtype is handled and raises a compile error if you add a new subtype without updating the switch.
// HTTP status classifier
record HttpStatus(int code, String phrase) {}
String classify(int code) {
return switch (code / 100) {
case 1 -> "Informational";
case 2 -> "Success";
case 3 -> "Redirection";
case 4 -> "Client Error";
case 5 -> "Server Error";
default -> "Unknown";
};
}
// Switch expression assigning to a var
var description = switch (shape) {
case Circle c -> "Circle with radius " + c.radius();
case Rectangle r -> "Rectangle " + r.width() + "x" + r.height();
default -> "Unknown shape";
};
// Nested switch expressions
String priority = switch (severity) {
case CRITICAL -> switch (environment) {
case PRODUCTION -> "P0 - Page immediately";
case STAGING -> "P1 - Fix before release";
default -> "P2 - Fix soon";
};
case HIGH -> "P2 - Fix soon";
case MEDIUM -> "P3 - Schedule";
case LOW -> "P4 - Backlog";
};
Switch expressions can be used anywhere a value is expected — as a method argument, in a variable initializer, in a return statement, or even as the initializer of another switch.