Switch Expressions
The switch statement has been part of Java since day one, inherited from C. It was clunky, error-prone (fall-through!), and verbose.
Java 14 standardized Switch Expressions, turning switch from a control flow statement into a value-producing expression.
1. The Problems with Old Switch
- Fall-through by default: Forgot a
break? Bugs happen. - Statement, not Expression: Can’t assign the result to a variable directly.
- Scope pollution: Variables defined in one case leak to others.
// The "Dark Ages"
String dayType;
switch (day) {
case MONDAY:
case FRIDAY:
dayType = "Work"; // Repeated assignment
break; // Easy to forget!
case SUNDAY:
dayType = "Rest";
break;
default:
dayType = "Unknown";
}
2. The New Arrow Syntax (->)
The arrow syntax -> executes only the code to its right. No fall-through. No break needed.
switch (day) {
case MONDAY, FRIDAY -> System.out.println("Work");
case SUNDAY -> System.out.println("Rest");
default -> System.out.println("Unknown");
}
3. Switch as an Expression
You can now capture the result of a switch directly into a variable.
// Clean & Concise
String dayType = switch (day) {
case MONDAY, FRIDAY -> "Work";
case SUNDAY -> "Rest";
default -> "Unknown";
};
Exhaustiveness Checking
When used as an expression, the compiler ensures you cover all possible values.
- For
enums: You must cover all constants (or havedefault). - For
sealedclasses: You must cover all permitted subclasses.
If you miss a case, the code will not compile. This is a huge win for safety.
4. Hardware Reality: Under the Hood
How does switch compare to if-else?
Bytecode: tableswitch vs lookupswitch
When you compile a switch statement, the JVM chooses between two opcodes:
tableswitch(O(1)): Used when case values are dense (e.g., 1, 2, 3). It creates a jump table (an array of memory addresses). The JVM just calculatesoffset + valueand jumps. It’s instantaneous.lookupswitch(O(log n)): Used when values are sparse (e.g., 1, 1000, 50000). It performs a binary search on the sorted keys.
If-Else Chains are always O(N) (linear scan).
[!NOTE] Modern Switch Expressions use
invokedynamicto bootstrap the logic, especially for Pattern Matching, allowing the JVM to optimize the dispatch strategy at runtime based on the actual data distribution.
5. The yield Keyword
Sometimes a single expression isn’t enough. You need a code block { ... } to calculate the result. In these blocks, use yield to return the value.
int result = switch (mode) {
case "A" -> 1;
case "B" -> 2;
case "C" -> {
System.out.println("Calculating C..."); // Side effect
int calculated = 1 + 2;
yield calculated; // Return value
}
default -> 0;
};
[!NOTE]
yieldis a context-sensitive keyword. You can still have a variable namedyieldelsewhere (though you shouldn’t!).
6. Interactive Demo: Refactoring to Modern Switch
Click the buttons to refactor the legacy code step-by-step.
7. Best Practices
- Prefer Arrow Syntax: It’s safer and cleaner.
- Use as Expression: Assign the result or
returnit directly. - Avoid Default with Enums: If you cover all enum constants, omit
default. That way, if you add a new enum constant later, the compiler will warn you!
// Good: Compile error if 'WEDNESDAY' is added to Day enum
String type = switch (day) {
case MONDAY, TUESDAY, THURSDAY, FRIDAY -> "Work";
case SATURDAY, SUNDAY -> "Weekend";
};