Switch in Java: From Statements to Expressions
Introduction
The switch construct in Java has undergone massive evolution. Originally, it was a simple control flow statement inherited from C/C++, notoriously verbose and error-prone due to fall-through behavior. However, starting with Java 14 and culminating in Java 21, switch has been completely revamped into a modern, powerful, and expressive feature that supports pattern matching, exhaustiveness checking, and expression evaluation.
This guide explores the evolution of switch, how to use modern switch expressions, and pattern matching.
1. The Traditional Switch Statement (Pre-Java 14)
The classic switch statement evaluates a variable and executes the corresponding case block. It requires explicit break statements to prevent "fall-through" (where execution continues into the next case).
int day = 3;
String dayName;
switch (day) {
case 1:
dayName = "Monday";
break;
case 2:
dayName = "Tuesday";
break;
case 3:
dayName = "Wednesday";
break;
default:
dayName = "Invalid Day";
break;
}
Drawbacks: Verbose, repetitive, and missing a break introduces subtle runtime bugs.
2. Switch Expressions (Java 14+)
Java 14 introduced Switch Expressions, fundamentally changing how switch operates.
- Arrow Syntax (
->): Eliminates the need forbreak(no fall-through). - Returns a Value: The
switchcan now be used as an expression on the right side of an assignment. - Multiple Labels: You can group multiple conditions on a single line.
int day = 3;
// Elegant, concise, and safe
String dayName = switch (day) {
case 1 -> "Monday";
case 2 -> "Tuesday";
case 3 -> "Wednesday";
case 4, 5 -> "Thursday or Friday"; // Grouping cases
case 6, 7 -> "Weekend";
default -> "Invalid Day"; // Exhaustiveness is required for expressions
};
The yield Keyword
If a particular case requires a block of code (multiple lines) before returning a value, use the yield keyword instead of return.
String status = switch (statusCode) {
case 200, 201 -> "Success";
case 404 -> {
System.out.println("Resource not found logged.");
yield "Not Found"; // Yields the value to the switch expression
}
default -> "Unknown Status";
};
3. Pattern Matching for Switch (Java 21+)
Java 21 finalized Pattern Matching for switch, which brings functional programming concepts to Java. It allows a switch to evaluate the Type of an object and automatically cast it, eliminating verbose instanceof checks.
Object obj = "Hello Java 21";
String result = switch (obj) {
case Integer i -> "It is an integer: " + i;
case String s -> "It is a string of length: " + s.length();
case null -> "It is null!"; // Switch can now handle null explicitly
default -> "Unknown object type";
};
Guarded Patterns (when clause)
You can add boolean conditions directly to cases using the when keyword.
String evaluateTemperature(Object temp) {
return switch (temp) {
case Integer i when i > 100 -> "Boiling";
case Integer i when i < 0 -> "Freezing";
case Integer i -> "Normal";
default -> "Invalid Reading";
};
}
4. Sealed Classes and Exhaustiveness
When combined with Sealed Classes, modern switch expressions guarantee exhaustiveness at compile time. If you handle all possible permitted subclasses in the switch, the compiler knows you don't need a default branch.
public sealed interface Shape permits Circle, Square {}
public final class Circle implements Shape { public double radius = 5; }
public final class Square implements Shape { public double side = 4; }
// No 'default' branch needed! The compiler ensures exhaustiveness.
double getArea(Shape shape) {
return switch (shape) {
case Circle c -> Math.PI * c.radius * c.radius;
case Square s -> s.side * s.side;
};
}
Summary
- Avoid traditional switch statements with colons and
breakin modern codebases. - Use Switch Expressions (
->) for cleaner, safer code without fall-through bugs. - Use
yieldwhen returning a value from a multi-line case block. - Leverage Pattern Matching in Java 21+ to dispatch logic based on object types cleanly.