1. The Hook (The "Byte-Sized" Intro)
- In a Nutshell: Switch expressions (Java 14+) = switch returns value! Arrow syntax (->) prevents fall-through. yield keyword returns value from block. Multiple labels (case 1, 2, 3 ->). Exhaustiveness checking (all cases covered).
- Pattern matching (Java 17+): Type patterns in switch.
- Benefits: No break needed, expression (not just statement), type-safe, exhaustive.
- Replaces: Traditional switch with breaks (10 lines → 3).
- Use for: Enum handling, polymorphic dispatch, state machines!
Think of vending machine. Old switch = pull lever, item drops, manually close door (break). New switch expression = smart vending: press button, auto-dispense + close (no fall-through). Returns item directly!
2. Conceptual Clarity (The "Simple" Tier)
💡 The Analogy
- Old switch (statement): Recipe with steps (do this, then that, remember to turn off stove!)
- New switch (expression): Function (input → output, automatically done)
3. Technical Mastery (The "Deep Dive")
Switch Expression Features
| Feature | Old Switch | New Switch Expression |
|---|---|---|
| Returns value | ❌ No | ✅ Yes |
| Fall-through | ✅ Default | ❌ No (arrow syntax) |
| break needed | ✅ Yes | ❌ No |
| Exhaustiveness | ❌ Optional | ✅ Required |
| Pattern matching | ❌ No | ✅ Yes (Java 17+) |
4. Interactive & Applied Code
java
public class SwitchExpressionsDemo {
public static void main(String[] args) {
demonstrateBasicSwitch();
demonstrateYield();
demonstrateExhaustiveness();
demonstratePatternMatching();
}
// ❌ BEFORE: Traditional switch (verbose)
static String oldSwitch(int day) {
String result;
switch (day) {
case 1:
result = "Monday";
break;
case 2:
result = "Tuesday";
break;
case 3:
result = "Wednesday";
break;
default:
result = "Other";
break;
}
return result;
}
// ✅ AFTER: Switch expression (concise)
static String newSwitch(int day) {
return switch (day) {
case 1 -> "Monday";
case 2 -> "Tuesday";
case 3 -> "Wednesday";
default -> "Other";
};
}
static void demonstrateBasicSwitch() {
System.out.println("=== BASIC SWITCH EXPRESSION ===");
int day = 2;
// Direct assignment
String dayName = switch (day) {
case 1 -> "Monday";
case 2 -> "Tuesday";
case 3, 4, 5 -> "Midweek"; // Multiple labels
case 6, 7 -> "Weekend";
default -> "Invalid";
};
System.out.println("Day: " + dayName);
}
// yield keyword (for multi-statement cases)
static void demonstrateYield() {
System.out.println("\n=== YIELD KEYWORD ===");
int num = 5;
String result = switch (num) {
case 1, 2, 3 -> "Small";
case 4, 5, 6 -> {
String temp = "Medium";
System.out.println("Processing: " + temp);
yield temp; // Return value from block
}
default -> {
System.out.println("Large number");
yield "Large";
}
};
System.out.println("Result: " + result);
}
// Exhaustiveness with enums
enum TrafficLight { RED, YELLOW, GREEN }
static void demonstrateExhaustiveness() {
System.out.println("\n=== EXHAUSTIVENESS ===");
TrafficLight light = TrafficLight.RED;
// ✅ Exhaustive (no default needed for enums)
String action = switch (light) {
case RED -> "Stop";
case YELLOW -> "Caution";
case GREEN -> "Go";
// No default needed! Compiler knows all values
};
System.out.println("Action: " + action);
}
// Pattern matching in switch (Java 17+)
static void demonstratePatternMatching() {
System.out.println("\n=== PATTERN MATCHING ===");
Object obj = 42;
String type = switch (obj) {
case Integer i -> "Integer: " + i;
case String s -> "String: " + s;
case Double d -> "Double: " + d;
case null -> "null";
default -> "Unknown";
};
System.out.println(type);
// With guarded patterns
Object value = "Hello";
String description = switch (value) {
case String s when s.length() < 5 -> "Short string";
case String s -> "Long string: " + s;
case Integer i when i > 0 -> "Positive number";
case Integer i -> "Non-positive number";
default -> "Other";
};
System.out.println(description);
}
}
// Real-world example: Calculator
class Calculator {
enum Operation { ADD, SUBTRACT, MULTIPLY, DIVIDE }
static double calculate(double a, double b, Operation op) {
return switch (op) {
case ADD -> a + b;
case SUBTRACT -> a - b;
case MULTIPLY -> a * b;
case DIVIDE -> {
if (b == 0) {
throw new ArithmeticException("Division by zero");
}
yield a / b;
}
};
}
public static void main(String[] args) {
System.out.println("5 + 3 = " + calculate(5, 3, Operation.ADD));
System.out.println("5 * 3 = " + calculate(5, 3, Operation.MULTIPLY));
}
}
// State machine with sealed classes
sealed interface State permits Idle, Running, Paused {}
record Idle() implements State {}
record Running(int progress) implements State {}
record Paused(int savedProgress) implements State {}
class StateMachine {
State state = new Idle();
void start() {
state = new Running(0);
}
String getStatus() {
return switch (state) {
case Idle i -> "System idle";
case Running(int progress) -> "Running: " + progress + "%";
case Paused(int saved) -> "Paused at: " + saved + "%";
};
}
}5. The Comparison & Decision Layer
| Scenario | Old Switch | New Switch Expression |
|---|---|---|
| Return value | Assign in each case | Direct return |
| Multiple labels | Multiple case lines | case 1, 2, 3 -> |
| Multi-statement | break | yield |
| Exhaustiveness | Optional default | Required |
6. The "Interview Corner" (The Edge)
The "Killer" Interview Question: "When do you need yield vs arrow syntax?" Answer:
- Arrow (
->): Single expression - yield: Multi-statement block
java
// ✅ Arrow: Single expression
String result = switch (x) {
case 1 -> "one"; // Simple expression
case 2 -> "two";
default -> "other";
};
// ✅ yield: Multi-statement block
String result = switch (x) {
case 1 -> {
System.out.println("Processing 1");
String temp = "one";
yield temp.toUpperCase(); // Must use yield!
}
default -> "other";
};
// ❌ ERROR: Can't implicitly return from block
// case 1 -> { "one"; } // Missing yield!Pro-Tip: Exhaustiveness with sealed types:
java
sealed interface Shape permits Circle, Square {}
// ✅ No default needed (exhaustive)
double area(Shape shape) {
return switch (shape) {
case Circle c -> Math.PI * c.radius() * c.radius();
case Square s -> s.side() * s.side();
// Compiler knows these are ALL types!
};
}