☕ Java

Java Switch — Statement, Expression, Pattern Matching & Best Practices

Everything you need to know about Java Switch — the classic switch statement with fall-through and break, the enhanced switch expression (Java 14+) with arrow labels and yield, String and Enum in switch, pattern matching switch with guarded patterns (Java 21), null handling, and real-world production code examples.

📅

Last Updated

March 2026

⏱️

Read Time

20 min

🎯

Level

Beginner

🏷️

Chapter

8 of 35

What is a Switch Statement in Java?

A switch statement is a control flow construct that evaluates a single expression and branches execution to the matching case label. It is a cleaner alternative to a long if-else-if chain when you are comparing one variable against multiple constant values. Instead of writing condition after condition, switch lets you list each possible value as a distinct case and execute the corresponding block.

Java's switch has evolved significantly across versions: the classic switch statement (all Java versions) handles control flow with explicit break statements and optional fall-through. The enhanced switch expression (Java 14+) produces a value, uses concise arrow syntax, and eliminates fall-through by default. The pattern matching switch (Java 21) adds type patterns and guarded conditions, making switch a powerful tool for data-driven dispatch.

☕ JavaSwitchIntro.java
public class SwitchIntro {
    public static void main(String[] args) {

        int day = 3;

        // ── Classic switch statement ───────────────────────────────────────
        switch (day) {
            case 1:
                System.out.println("Monday");
                break;
            case 2:
                System.out.println("Tuesday");
                break;
            case 3:
                System.out.println("Wednesday");
                break;
            default:
                System.out.println("Other day");
        }
        // Output: Wednesday

        // ── Enhanced switch expression (Java 14+) — cleaner ───────────────
        String dayName = switch (day) {
            case 1 -> "Monday";
            case 2 -> "Tuesday";
            case 3 -> "Wednesday";
            case 4 -> "Thursday";
            case 5 -> "Friday";
            case 6 -> "Saturday";
            case 7 -> "Sunday";
            default -> "Invalid day";
        };
        System.out.println(dayName); // Wednesday
    }
}

Switch Syntax & Structure

The classic switch statement has a straightforward structure: the keyword switch, followed by the selector expression in parentheses, followed by a block containing one or more case labels, an optional default label, and statements. Each case matches a constant value — not a range or condition — and execution jumps to the matching label.

☕ JavaSwitchSyntax.java
public class SwitchSyntax {
    public static void main(String[] args) {

        // ── Syntax of classic switch statement ───────────────────────────
        // switch (selectorExpression) {
        //     case constant1:
        //         statements;
        //         break;       // Prevents fall-through
        //     case constant2:
        //         statements;
        //         break;
        //     default:         // Optional — runs if no case matches
        //         statements;
        // }

        // ── Example with int ──────────────────────────────────────────────
        int month = 4;
        switch (month) {
            case 1:  System.out.println("January");   break;
            case 2:  System.out.println("February");  break;
            case 3:  System.out.println("March");     break;
            case 4:  System.out.println("April");     break;
            case 5:  System.out.println("May");       break;
            case 6:  System.out.println("June");      break;
            case 7:  System.out.println("July");      break;
            case 8:  System.out.println("August");    break;
            case 9:  System.out.println("September"); break;
            case 10: System.out.println("October");   break;
            case 11: System.out.println("November");  break;
            case 12: System.out.println("December");  break;
            default: System.out.println("Invalid month");
        }
        // Output: April

        // ── Key structural rules ─────────────────────────────────────────
        // 1. case values must be compile-time constants (literals or final vars)
        // 2. case values must be unique — no duplicate case labels
        // 3. default is optional but recommended
        // 4. default can appear anywhere — not just at the end
        // 5. Multiple statements allowed per case without braces
        // 6. Empty case (no statements) falls through to the next case

        // ── case with compile-time constant variable ───────────────────────
        final int JAN = 1;
        int m = 1;
        switch (m) {
            case JAN:  // ✅ Allowed — JAN is a compile-time constant (final)
                System.out.println("January");
                break;
            default:
                System.out.println("Other");
        }
    }
}

How Switch Executes — Control Flow

Understanding the execution flow of a switch is essential — especially the role of break and fall-through. When a switch is executed: (1) The selector expression is evaluated once. (2) Java compares the result against each case label from top to bottom. (3) When a match is found, execution jumps to that case and runs the statements. (4) Execution continues downward through subsequent cases until a break, return, or the end of the switch block is reached. (5) If no case matches, the default case runs (if present).

☕ JavaSwitchFlow.java
public class SwitchFlow {
    public static void main(String[] args) {

        // ── Normal flow: break stops execution at each case ───────────────
        int x = 2;
        switch (x) {
            case 1: System.out.println("One");   break;
            case 2: System.out.println("Two");   break; // ← Matches here
            case 3: System.out.println("Three"); break; // ← Never reached
            default: System.out.println("Other");
        }
        // Output: Two

        // ── No match — default executes ────────────────────────────────────
        int y = 99;
        switch (y) {
            case 1: System.out.println("One"); break;
            case 2: System.out.println("Two"); break;
            default: System.out.println("No match — default runs");
        }
        // Output: No match — default runs

        // ── No default, no match — switch does nothing ────────────────────
        int z = 99;
        switch (z) {
            case 1: System.out.println("One"); break;
            case 2: System.out.println("Two"); break;
        }
        // Output: (nothing — no match, no default)

        // ── default NOT at the end — still only runs if no case matches ────
        int a = 3;
        switch (a) {
            default: System.out.println("Default"); break;
            case 1:  System.out.println("One");     break;
            case 3:  System.out.println("Three");   break;
        }
        // Output: Three  (case 3 matches — default skipped even though it's first)

        // ── return inside switch — exits the method ───────────────────────
        System.out.println(getDayType(6));  // Weekend
    }

    static String getDayType(int day) {
        switch (day) {
            case 1: case 2: case 3: case 4: case 5:
                return "Weekday";   // return acts like break + exits method
            case 6: case 7:
                return "Weekend";
            default:
                return "Invalid";
        }
    }
}

break in Switch — Stopping Fall-Through

The break statement is used inside a classic switch to exit the switch block after a case executes. Without break, execution falls through to the next case — regardless of whether it matches — and continues until a break, return, or the end of the switch block is reached. This fall-through behaviour is a notorious source of bugs. Every case in a classic switch should end with break (or return) unless fall-through is deliberately intended.

☕ JavaBreakInSwitch.java
public class BreakInSwitch {
    public static void main(String[] args) {

        // ── WITH break — correct, expected behaviour ───────────────────────
        int val = 2;
        switch (val) {
            case 1: System.out.println("One");   break;
            case 2: System.out.println("Two");   break;  // ← Exits after this
            case 3: System.out.println("Three"); break;  // ← Never reached
        }
        // Output: Two

        // ── WITHOUT break — fall-through happens ─────────────────────────
        int val2 = 2;
        switch (val2) {
            case 1: System.out.println("One");
            case 2: System.out.println("Two");    // ← Matches here
            case 3: System.out.println("Three"); // ← Also executes (fall-through!)
            case 4: System.out.println("Four");  // ← Also executes (fall-through!)
        }
        // Output:
        // Two
        // Three
        // Four

        // ── break exits only the switch, not an outer loop ────────────────
        for (int i = 1; i <= 3; i++) {
            switch (i) {
                case 2:
                    System.out.println("Found 2 — breaking switch");
                    break;  // Exits the switch, NOT the for loop
                default:
                    System.out.println("i = " + i);
            }
        }
        // Output:
        // i = 1
        // Found 2 — breaking switch
        // i = 3

        // ── return instead of break — exits the entire method ────────────
        System.out.println(getLabel(2)); // Two
    }

    static String getLabel(int n) {
        switch (n) {
            case 1: return "One";
            case 2: return "Two";   // return exits method — no fall-through
            case 3: return "Three";
            default: return "Unknown";
        }
    }
}

default Case — Handling Unmatched Values

The default case is the switch equivalent of the else clause in an if-else chain. It executes when none of the case labels match the selector value. It is optional but strongly recommended — omitting it means unmatched values silently do nothing, which can hide bugs. The default case can appear anywhere in the switch block — not just at the end — but placing it last is the universally followed convention for readability. For switch expressions, default is usually required to ensure exhaustiveness.

☕ JavaDefaultCase.java
public class DefaultCase {
    public static void main(String[] args) {

        // ── default at the end — conventional position ────────────────────
        int code = 404;
        switch (code) {
            case 200: System.out.println("OK");                   break;
            case 201: System.out.println("Created");              break;
            case 400: System.out.println("Bad Request");          break;
            case 404: System.out.println("Not Found");            break;
            case 500: System.out.println("Internal Server Error"); break;
            default:  System.out.println("Unknown status code");
        }
        // Output: Not Found

        // ── default when no case matches ───────────────────────────────────
        int code2 = 301;
        switch (code2) {
            case 200: System.out.println("OK");        break;
            case 404: System.out.println("Not Found"); break;
            default:  System.out.println("Redirect or other: " + code2);
        }
        // Output: Redirect or other: 301

        // ── default at the top — valid but unconventional ─────────────────
        int val = 5;
        switch (val) {
            default:  System.out.println("Default runs"); break;
            case 1:   System.out.println("One");          break;
            case 2:   System.out.println("Two");          break;
        }
        // Output: Default runs  (val=5 matches no case — default executes)

        // ── default with fall-through from case ───────────────────────────
        int val2 = 1;
        switch (val2) {
            case 1:  System.out.println("One"); // No break — falls through
            default: System.out.println("Default also runs");
        }
        // Output:
        // One
        // Default also runs   (fall-through from case 1 into default)

        // ── Switch expression — default required for exhaustiveness ─────────
        int score = 85;
        String grade = switch (score / 10) {
            case 10, 9 -> "A";
            case 8     -> "B";
            case 7     -> "C";
            case 6     -> "D";
            default    -> "F";  // Required — int has infinite possible values
        };
        System.out.println("Grade: " + grade);  // Grade: B
    }
}

Fall-Through — Intentional & Accidental

Fall-through is the behaviour where, after a matched case executes, control continues into the next case without stopping — because there is no break. It is one of the most notorious Java pitfalls for beginners and experienced developers alike. However, fall-through is occasionally used intentionally to share code between consecutive cases — for example, grouping multiple values that produce the same result.

☕ JavaFallThrough.java
public class FallThrough {
    public static void main(String[] args) {

        // ── ACCIDENTAL fall-through — a common bug ────────────────────────
        int day = 2;
        switch (day) {
            case 1: System.out.println("Monday");
            case 2: System.out.println("Tuesday");   // ← Match here
            case 3: System.out.println("Wednesday"); // ← Fall-through!
            case 4: System.out.println("Thursday");  // ← Fall-through!
            default: System.out.println("Other");    // ← Fall-through!
        }
        // Output:
        // Tuesday
        // Wednesday
        // Thursday
        // Other
        // This is a BUG — programmer forgot break statements

        // ── INTENTIONAL fall-through — grouping cases ─────────────────────
        int month = 4;
        int daysInMonth;
        switch (month) {
            case 1: case 3: case 5: case 7:
            case 8: case 10: case 12:
                daysInMonth = 31;  // These months have 31 days
                break;
            case 4: case 6: case 9: case 11:
                daysInMonth = 30;  // These months have 30 days
                break;
            case 2:
                daysInMonth = 28;  // Simplified — ignoring leap year
                break;
            default:
                daysInMonth = -1;  // Invalid month
        }
        System.out.println("Days in month " + month + ": " + daysInMonth);
        // Output: Days in month 4: 30

        // ── INTENTIONAL fall-through with @SuppressWarnings annotation ─────
        int level = 1;
        @SuppressWarnings("fallthrough")
        String result;
        switch (level) {
            case 3: System.out.println("Senior features enabled");
            // fall through intentional
            case 2: System.out.println("Mid features enabled");
            // fall through intentional
            case 1: System.out.println("Basic features enabled");
                break;
            default: System.out.println("Guest access");
        }
        // Output for level=1:
        // Basic features enabled
        // Output for level=3:
        // Senior features enabled
        // Mid features enabled
        // Basic features enabled
    }
}

Multiple Case Labels — Grouping Cases

When multiple values should produce the same result, Java allows grouping case labels so they share a single block of code. In classic switch, this is done by stacking empty cases on top of each other (intentional fall-through). In enhanced switch (Java 14+), the cleaner approach is using comma-separated case labels: case 1, 2, 3 ->. The comma syntax is only available in enhanced switch (Java 14+) — it is not valid in classic switch colon-style cases.

☕ JavaMultipleCaseLabels.java
public class MultipleCaseLabels {
    public static void main(String[] args) {

        // ── Classic style: stacked cases (intentional fall-through) ─────────
        int day = 6;
        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:
                System.out.println("Invalid");
        }
        // Output: Weekend

        // ── Enhanced switch (Java 14+): comma-separated case labels ────────
        String dayType = switch (day) {
            case 1, 2, 3, 4, 5 -> "Weekday";  // Comma-separated — clean!
            case 6, 7          -> "Weekend";
            default            -> "Invalid";
        };
        System.out.println(dayType); // Weekend

        // ── Grouping with String values ───────────────────────────────────
        String season = "SUMMER";
        String weather = switch (season) {
            case "SPRING", "AUTUMN"            -> "Mild";
            case "SUMMER"                       -> "Hot";
            case "WINTER"                       -> "Cold";
            default                             -> "Unknown season";
        };
        System.out.println(weather); // Hot

        // ── Grouping months for quarter calculation ───────────────────────
        int month = 8;
        int quarter = switch (month) {
            case 1, 2, 3   -> 1;
            case 4, 5, 6   -> 2;
            case 7, 8, 9   -> 3;
            case 10, 11, 12 -> 4;
            default        -> throw new IllegalArgumentException("Invalid month: " + month);
        };
        System.out.println("Quarter: " + quarter); // Quarter: 3
    }
}

String in Switch — Java 7+

Since Java 7, String is a valid selector type in switch. Before Java 7, only integral types and enum were allowed. String switch uses .equals() internally for comparison — it is case-sensitive ("Monday" and "monday" are different cases). A critical safety rule: always null-check before switching on a String — if the String is null, the switch throws a NullPointerException.

☕ JavaStringInSwitch.java
public class StringInSwitch {
    public static void main(String[] args) {

        // ── String switch — basic usage ───────────────────────────────────
        String command = "START";
        switch (command) {
            case "START":
                System.out.println("Starting application...");
                break;
            case "STOP":
                System.out.println("Stopping application...");
                break;
            case "RESTART":
                System.out.println("Restarting application...");
                break;
            default:
                System.out.println("Unknown command: " + command);
        }
        // Output: Starting application...

        // ── Case-sensitive — 'start' is NOT 'START' ──────────────────────
        String cmd2 = "start";
        switch (cmd2) {
            case "START": System.out.println("Matched START"); break;
            default:      System.out.println("No match — case sensitive!");
        }
        // Output: No match — case sensitive!

        // ── Safe pattern: normalize before switch ─────────────────────────
        String userInput = "  start  ";
        switch (userInput.trim().toUpperCase()) {  // Normalize first
            case "START":   System.out.println("Starting...");   break;
            case "STOP":    System.out.println("Stopping...");   break;
            default:        System.out.println("Unknown");
        }
        // Output: Starting...

        // ── NullPointerException if String is null ────────────────────────
        String s = null;
        // switch (s) { ... }  // ❌ NullPointerException at runtime!

        // ✅ Always null-check before switching on String
        if (s != null) {
            switch (s) {
                case "A": System.out.println("A"); break;
                default:  System.out.println("Other");
            }
        } else {
            System.out.println("Null input — using default");
        }
        // Output: Null input — using default

        // ── Enhanced switch with String (Java 14+) ────────────────────────
        String day = "MONDAY";
        String type = switch (day) {
            case "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY" -> "Weekday";
            case "SATURDAY", "SUNDAY" -> "Weekend";
            default -> "Unknown";
        };
        System.out.println(type); // Weekday
    }
}

Enum in Switch — The Cleanest Switch Pattern

Using an enum as the switch selector is one of the most powerful and clean patterns in Java. Enums provide a closed, compile-time-known set of values — the compiler can verify exhaustiveness and warn if new enum constants are not handled. When all enum constants are covered in cases, default is not needed (though still recommended for future-proofing). Enum in switch is the preferred approach when a variable can take one of a fixed set of named states.

☕ JavaEnumInSwitch.java
public class EnumInSwitch {

    enum Day { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }

    enum OrderStatus { PENDING, CONFIRMED, SHIPPED, DELIVERED, CANCELLED }

    public static void main(String[] args) {

        // ── Classic switch with enum ───────────────────────────────────────
        Day today = Day.WEDNESDAY;
        switch (today) {
            case MONDAY:
            case TUESDAY:
            case WEDNESDAY:
            case THURSDAY:
            case FRIDAY:
                System.out.println("Weekday — work day");
                break;
            case SATURDAY:
            case SUNDAY:
                System.out.println("Weekend — rest day");
                break;
            // Note: no 'default' needed — all enum values covered
        }
        // Output: Weekday — work day

        // ── Enhanced switch expression with enum ──────────────────────────
        String schedule = switch (today) {
            case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> "9 AM - 5 PM";
            case SATURDAY -> "10 AM - 2 PM";
            case SUNDAY   -> "Closed";
            // No default needed — all enum constants covered (exhaustive)
        };
        System.out.println("Schedule: " + schedule);
        // Output: Schedule: 9 AM - 5 PM

        // ── Enum switch for order processing ──────────────────────────────
        OrderStatus status = OrderStatus.SHIPPED;
        String message = switch (status) {
            case PENDING   -> "Your order is awaiting confirmation.";
            case CONFIRMED -> "Your order has been confirmed!";
            case SHIPPED   -> "Your order is on the way!";
            case DELIVERED -> "Your order has been delivered. Enjoy!";
            case CANCELLED -> "Your order has been cancelled.";
        };
        System.out.println(message);
        // Output: Your order is on the way!

        // ── Enum switch with method body — multi-statement ────────────────
        processOrder(OrderStatus.CONFIRMED);
    }

    static void processOrder(OrderStatus status) {
        switch (status) {
            case PENDING:
                System.out.println("Sending confirmation email...");
                System.out.println("Reserving inventory...");
                break;
            case CONFIRMED:
                System.out.println("Preparing shipment...");
                System.out.println("Notifying warehouse...");
                break;
            case SHIPPED:
                System.out.println("Updating tracking info...");
                break;
            case DELIVERED:
                System.out.println("Sending feedback request...");
                break;
            case CANCELLED:
                System.out.println("Processing refund...");
                break;
        }
    }
}

Enhanced Switch Expression — Java 14+

Java 14 introduced the switch expression as a standard feature (JEP 361). Unlike the classic switch statement (which executes code), a switch expression produces a value that can be assigned, returned, or used in another expression. Switch expressions use arrow labels (case X ->) that eliminate fall-through by default, require exhaustiveness (covering all possible values), and are significantly more concise than the classic form.

☕ JavaEnhancedSwitchExpression.java
public class EnhancedSwitchExpression {
    public static void main(String[] args) {

        // ── Basic switch expression — produces a value ────────────────────
        int day = 3;
        String dayName = switch (day) {
            case 1 -> "Monday";
            case 2 -> "Tuesday";
            case 3 -> "Wednesday";
            case 4 -> "Thursday";
            case 5 -> "Friday";
            case 6 -> "Saturday";
            case 7 -> "Sunday";
            default -> "Invalid";
        };  // Note: semicolon after closing brace
        System.out.println(dayName); // Wednesday

        // ── Switch expression inline in print ─────────────────────────────
        int score = 75;
        System.out.println("Grade: " + switch (score / 10) {
            case 10, 9 -> "A";
            case 8     -> "B";
            case 7     -> "C";
            case 6     -> "D";
            default    -> "F";
        });
        // Output: Grade: C

        // ── Switch expression as method return ────────────────────────────
        System.out.println(getDayType(6)); // Weekend
        System.out.println(getDayType(3)); // Weekday

        // ── Switch expression with int result ─────────────────────────────
        String month = "APRIL";
        int days = switch (month) {
            case "JANUARY", "MARCH", "MAY", "JULY",
                 "AUGUST", "OCTOBER", "DECEMBER" -> 31;
            case "APRIL", "JUNE", "SEPTEMBER", "NOVEMBER" -> 30;
            case "FEBRUARY" -> 28;
            default -> throw new IllegalArgumentException("Unknown month: " + month);
        };
        System.out.println(month + " has " + days + " days.");
        // Output: APRIL has 30 days.

        // ── Switch expression: no fall-through — each arrow case independent
        int x = 2;
        String result = switch (x) {
            case 1 -> "One";
            case 2 -> "Two";    // Only this executes — no fall-through
            case 3 -> "Three";  // Never reached for x=2
            default -> "Other";
        };
        System.out.println(result); // Two
    }

    static String getDayType(int day) {
        return switch (day) {          // switch expression returned directly
            case 1, 2, 3, 4, 5 -> "Weekday";
            case 6, 7          -> "Weekend";
            default            -> "Invalid";
        };
    }
}

Arrow Labels — case X -> Syntax

Arrow labels (case X ->) are the new concise syntax introduced in Java 14. They replace the traditional case X: colon syntax with three key improvements: no fall-through (each arrow case is self-contained), concise single-line expressions (no need for break), and block support for multi-statement cases using yield. Arrow labels can be used in both switch expressions and switch statements.

☕ JavaArrowLabels.java
public class ArrowLabels {
    public static void main(String[] args) {

        // ── Arrow label: single expression ───────────────────────────────
        int x = 3;
        String result = switch (x) {
            case 1 -> "One";    // Expression after -> is the value
            case 2 -> "Two";
            case 3 -> "Three";
            default -> "Other";
        };
        System.out.println(result); // Three

        // ── Arrow label: block body with yield ────────────────────────────
        int score = 72;
        String grade = switch (score / 10) {
            case 10, 9 -> "A";
            case 8     -> "B";
            case 7     -> {
                // Multi-statement block — use yield to produce value
                String base = "C";
                System.out.println("Computing grade for score: " + score);
                yield base;  // yield returns the value from this block
            }
            case 6 -> "D";
            default -> {
                System.out.println("Score " + score + " is below passing.");
                yield "F";
            }
        };
        System.out.println("Grade: " + grade);
        // Output:
        // Computing grade for score: 72
        // Grade: C

        // ── Arrow label: throwing exceptions ──────────────────────────────
        int month = 13;
        int daysInMonth = switch (month) {
            case 1, 3, 5, 7, 8, 10, 12 -> 31;
            case 4, 6, 9, 11            -> 30;
            case 2                      -> 28;
            default -> throw new IllegalArgumentException(
                "Invalid month: " + month
            );
        };

        // ── Arrow label in switch STATEMENT (not expression) ──────────────
        // Arrow labels can also be used without assigning a value
        String cmd = "DEPLOY";
        switch (cmd) {
            case "BUILD"  -> System.out.println("Building...");
            case "TEST"   -> System.out.println("Testing...");
            case "DEPLOY" -> System.out.println("Deploying...");
            default       -> System.out.println("Unknown command: " + cmd);
        }
        // Output: Deploying...

        // ── Comparison: old style vs arrow style ─────────────────────────
        // OLD (classic switch statement):
        // switch(x) { case 3: result = "Three"; break; ... }

        // NEW (arrow switch expression) — same result, more concise:
        // String r = switch(x) { case 3 -> "Three"; default -> "?"; };
    }
}

yield Keyword in Switch Expression

The yield keyword (Java 14+) is used inside a switch expression to produce (return) a value from a multi-statement case block. It is analogous to return but scoped to the switch expression — it does not exit the enclosing method. yield is only valid in switch expressions, not in switch statements. When using arrow labels (->) with a single expression, yield is implicit and not needed. yield is only required when a case uses a {} block body with multiple statements.

☕ JavaYieldKeyword.java
public class YieldKeyword {
    public static void main(String[] args) {

        // ── yield in arrow-label block ────────────────────────────────────
        int score = 85;
        String grade = switch (score / 10) {
            case 10, 9 -> "A";
            case 8     -> {
                System.out.println("Good performance!");
                yield "B";  // yield provides the value for this case block
            }
            case 7 -> "C";
            default -> {
                System.out.println("Below B grade");
                yield "D or F";
            }
        };
        System.out.println("Grade: " + grade);
        // Output:
        // Good performance!
        // Grade: B

        // ── yield in colon-style case (traditional labels in switch expression)
        int day = 4;
        String dayType = switch (day) {
            case 1:
            case 2:
            case 3:
            case 4:
            case 5:
                yield "Weekday";   // yield in colon-style case
            case 6:
            case 7:
                yield "Weekend";
            default:
                yield "Invalid";
        };
        System.out.println(dayType); // Weekday

        // ── yield vs return — key difference ─────────────────────────────
        System.out.println(computeDiscount("PREMIUM")); // 20
        System.out.println(computeDiscount("REGULAR")); // 5
    }

    static int computeDiscount(String customerType) {
        int baseDiscount = switch (customerType) {
            case "PREMIUM" -> {
                int bonus = 5;
                int base  = 15;
                yield base + bonus;   // yield — provides value to switch expression
                // return base + bonus; // ❌ Would exit the method, not just switch
            }
            case "REGULAR" -> {
                yield 5;
            }
            default -> 0;
        };
        return baseDiscount;  // return exits the method
    }
}

switch vs if-else — When to Use Which

Both switch and if-else handle multi-way branching, but each has strengths and appropriate use cases. The key difference: switch tests a single expression against constant values, while if-else can test arbitrary conditions including ranges, compound conditions, and non-constant comparisons.

Aspectswitchif-else
TestsOne expression vs constant valuesAny boolean condition
Ranges❌ Cannot test ranges (x > 10)✅ Can test ranges (x > 10 && x < 20)
Null safety⚠️ NPE if selector is null✅ Null-safe (null == null is false)
Supported typesint, char, String, enum, patternsAny type and expression
Fall-through⚠️ Classic: yes. Enhanced: no❌ No fall-through possible
Produces value✅ Switch expression (Java 14+)❌ Cannot produce value directly
Readability✅ Cleaner for many fixed values✅ Cleaner for complex logic
Performance✅ O(1) bytecode (tableswitch)⚠️ O(n) — sequential evaluation
Exhaustiveness✅ Compiler-enforced (expression)❌ Not enforced
Pattern matching✅ Java 21 patterns and guards✅ instanceof (Java 16+)
Use caseStatus/state/mode/fixed valuesRanges, compound conditions, flags
☕ JavaSwitchVsIfElse.java
public class SwitchVsIfElse {
    public static void main(String[] args) {

        // ── USE switch: many values of a single variable ──────────────────
        String status = "SHIPPED";

        // if-else version — verbose:
        if (status.equals("PENDING"))        { System.out.println("Awaiting"); }
        else if (status.equals("CONFIRMED")) { System.out.println("Confirmed"); }
        else if (status.equals("SHIPPED"))   { System.out.println("On the way"); }
        else if (status.equals("DELIVERED")) { System.out.println("Delivered"); }
        else                                  { System.out.println("Unknown"); }

        // switch version — cleaner:
        switch (status) {
            case "PENDING"   -> System.out.println("Awaiting");
            case "CONFIRMED" -> System.out.println("Confirmed");
            case "SHIPPED"   -> System.out.println("On the way");
            case "DELIVERED" -> System.out.println("Delivered");
            default          -> System.out.println("Unknown");
        }

        // ── USE if-else: ranges and compound conditions ───────────────────
        int temperature = 35;

        // switch CANNOT do this — no range support:
        // switch (temperature) { case > 30: ... }  // ❌ Compile error

        // if-else is the correct choice for ranges:
        if (temperature > 40) {
            System.out.println("Extreme heat");
        } else if (temperature > 30) {
            System.out.println("Hot");
        } else if (temperature > 20) {
            System.out.println("Warm");
        } else {
            System.out.println("Cool or Cold");
        }
        // Output: Hot

        // ── USE switch expression to assign result ────────────────────────
        int dayNum = 6;
        boolean isWeekend = switch (dayNum) {
            case 6, 7  -> true;
            default    -> false;
        };
        // if-else equivalent is more verbose for assignment:
        // boolean isWeekend = (dayNum == 6 || dayNum == 7);
        System.out.println("Weekend: " + isWeekend); // Weekend: true
    }
}

Pattern Matching Switch — Java 21

Pattern matching for switch (JEP 441, standard in Java 21) extends switch to work with type patterns — the same patterns used in instanceof. This allows you to test what type an object is AND bind it to a typed variable in one step, inside a switch. It is the modern, exhaustive, and null-safe alternative to long chains of if (x instanceof TypeA a) ... else if (x instanceof TypeB b). Combined with guarded patterns (when clauses) and sealed class hierarchies, pattern matching switch is extremely powerful.

☕ JavaPatternMatchingSwitch.java
public class PatternMatchingSwitch {

    sealed interface Shape permits Circle, Rectangle, Triangle {}
    record Circle(double radius)              implements Shape {}
    record Rectangle(double width, double height) implements Shape {}
    record Triangle(double base, double height)   implements Shape {}

    public static void main(String[] args) {

        // ── Pattern matching switch — type patterns ───────────────────────
        Object obj = "Hello, Java 21!";
        String description = switch (obj) {
            case Integer i  -> "Integer: " + i;
            case Double  d  -> "Double: " + d;
            case String  s  -> "String of length " + s.length();
            case null       -> "Null value";
            default         -> "Other type: " + obj.getClass().getSimpleName();
        };
        System.out.println(description);
        // Output: String of length 14

        // ── Pattern matching with sealed hierarchy ────────────────────────
        Shape shape = new Circle(5.0);
        double area = switch (shape) {
            case Circle    c -> Math.PI * c.radius() * c.radius();
            case Rectangle r -> r.width() * r.height();
            case Triangle  t -> 0.5 * t.base() * t.height();
            // No default needed — sealed hierarchy is exhaustive
        };
        System.out.printf("Area: %.2f%n", area);
        // Output: Area: 78.54

        // ── Pattern matching: replaces long instanceof chain ───────────────
        Object data = 3.14;
        // OLD WAY (verbose):
        // if (data instanceof Integer i) { System.out.println(i * 2); }
        // else if (data instanceof Double d) { System.out.println(d * 2); }
        // else if (data instanceof String s) { System.out.println(s.toUpperCase()); }

        // NEW WAY (pattern switch):
        switch (data) {
            case Integer i -> System.out.println(i * 2);
            case Double  d -> System.out.println(d * 2);       // 6.28
            case String  s -> System.out.println(s.toUpperCase());
            default        -> System.out.println("Unknown");
        }

        // ── Payment type dispatch with pattern matching ───────────────────
        processPayment(new CreditCardPayment(500.0, "9876"));
        processPayment(new UPIPayment(200.0, "user@upi"));
    }

    sealed interface Payment permits CreditCardPayment, UPIPayment {}
    record CreditCardPayment(double amount, String last4) implements Payment {}
    record UPIPayment(double amount, String upiId)        implements Payment {}

    static void processPayment(Payment p) {
        String receipt = switch (p) {
            case CreditCardPayment cc ->
                "Card ending " + cc.last4() + " charged ₹" + cc.amount();
            case UPIPayment upi ->
                "UPI " + upi.upiId() + " charged ₹" + upi.amount();
        };
        System.out.println(receipt);
    }
}

Guarded Patterns — when Clause (Java 21)

A guarded pattern adds an extra boolean condition to a type pattern using the when keyword. The case only matches if the type check AND the boolean condition both pass. This eliminates the need for nested if statements inside pattern match cases, making complex type-and-condition dispatch extremely concise. The when clause has access to the bound variable from the pattern.

☕ JavaGuardedPatterns.java
public class GuardedPatterns {
    public static void main(String[] args) {

        // ── Guarded pattern: type check + condition ────────────────────────
        Object obj = 42;
        String result = switch (obj) {
            case Integer i when i < 0     -> "Negative integer: " + i;
            case Integer i when i == 0    -> "Zero";
            case Integer i when i > 0
                             && i <= 100  -> "Positive integer 1-100: " + i;
            case Integer i               -> "Large integer: " + i;
            case String  s when s.isEmpty() -> "Empty string";
            case String  s               -> "String: " + s;
            case null                    -> "Null";
            default                      -> "Other: " + obj;
        };
        System.out.println(result); // Positive integer 1-100: 42

        // ── Guarded pattern with records ──────────────────────────────────
        record Product(String name, double price, int stock) {}

        Object item = new Product("Laptop", 75000.0, 0);
        String status = switch (item) {
            case Product p when p.stock() == 0  -> p.name() + " is OUT OF STOCK";
            case Product p when p.stock() < 5   -> p.name() + " is LOW STOCK ("+p.stock()+" left)";
            case Product p when p.price() > 50000 -> p.name() + " is PREMIUM (₹"+p.price()+")";
            case Product p                       -> p.name() + " — Available";
            default                             -> "Unknown item";
        };
        System.out.println(status);
        // Output: Laptop is OUT OF STOCK

        // ── Guarded patterns eliminate nested if inside case ──────────────
        // OLD WAY (nested if inside case):
        // case Integer i -> {
        //     if (i > 0) yield "Positive";
        //     else yield "Non-positive";
        // }

        // NEW WAY (guarded patterns):
        // case Integer i when i > 0 -> "Positive";
        // case Integer i            -> "Non-positive";

        // ── User role and permission dispatch ─────────────────────────────
        record User(String name, String role, boolean isActive) {}

        Object user = new User("Priya", "ADMIN", true);
        String access = switch (user) {
            case User u when !u.isActive()          -> "Account disabled";
            case User u when u.role().equals("ADMIN") -> u.name() + " — Full Access";
            case User u when u.role().equals("EDITOR") -> u.name() + " — Edit Access";
            case User u                             -> u.name() + " — Read Only";
            default                                 -> "Unknown user";
        };
        System.out.println(access); // Priya — Full Access
    }
}

Handling null in Switch — Java 21

In classic switch (all versions) and enhanced switch without pattern matching, passing null as the selector throws a NullPointerException. Java 21 pattern matching switch adds explicit case null support — you can handle the null case directly inside the switch instead of wrapping the switch in a null check. This makes pattern matching switch the first switch form that is fully null-safe when case null is included.

☕ JavaSwitchWithNull.java
public class SwitchWithNull {
    public static void main(String[] args) {

        // ── Classic switch: null throws NullPointerException ───────────────
        String s = null;
        // switch (s) { case "A": ... }  // ❌ NullPointerException!

        // ── Classic fix: null-check BEFORE switch ────────────────────────
        if (s != null) {
            switch (s) {
                case "A": System.out.println("A"); break;
                default:  System.out.println("Other");
            }
        } else {
            System.out.println("Null input");
        }

        // ── Java 21: case null inside pattern matching switch ─────────────
        Object obj = null;
        String result = switch (obj) {
            case null        -> "Null value received";
            case Integer i   -> "Integer: " + i;
            case String str  -> "String: " + str;
            default          -> "Other: " + obj;
        };
        System.out.println(result); // Null value received

        // ── case null combined with default ───────────────────────────────
        Object o2 = null;
        switch (o2) {
            case null, default -> System.out.println("Null or unmatched");
            case String s2     -> System.out.println("String: " + s2);
        }
        // Output: Null or unmatched

        // ── Practical: API response handling with null ────────────────────
        processResponse(null);
        processResponse("SUCCESS");
        processResponse("ERROR");
        processResponse("TIMEOUT");
    }

    static void processResponse(String response) {
        String action = switch (response) {
            case null      -> "No response received — retry";
            case "SUCCESS" -> "Process completed";
            case "ERROR"   -> "Handle error and alert team";
            default        -> "Unknown response: " + response;
        };
        System.out.println(action);
    }
}

Common Mistakes & Pitfalls — Switch Bugs That Fool Everyone

These are the most frequent switch-related mistakes in Java — from forgotten breaks to type confusion and null traps.

☕ JavaSwitchMistakes.java
// ❌ MISTAKE 1: Forgetting break — accidental fall-through
int x = 2;
switch (x) {
    case 1: System.out.println("One");
    case 2: System.out.println("Two");   // Executes
    case 3: System.out.println("Three"); // Also executes — bug!
}
// ✅ Fix: add break after every case, or use enhanced switch

// ❌ MISTAKE 2: Using non-constant in case label
int limit = 10;
// switch (x) { case limit: ... }  // ❌ Compile error — limit is not final
// ✅ Fix: make it final: final int LIMIT = 10;

// ❌ MISTAKE 3: Switching on null String — NullPointerException
String s = null;
// switch (s) { case "A": ... }  // ❌ NullPointerException at runtime
// ✅ Fix: null-check first, or use Java 21 case null

// ❌ MISTAKE 4: Using long, float, double, boolean as switch selector
// long l = 10L;
// switch (l) { ... }    // ❌ Compile error — long not supported
// float f = 1.0f;
// switch (f) { ... }    // ❌ Compile error — float not supported
// ✅ Supported: byte, short, int, char, String, enum, and patterns (Java 21)

// ❌ MISTAKE 5: Duplicate case values
// switch (x) {
//     case 5: ...;
//     case 5: ...;  // ❌ Compile error: duplicate case label
// }

// ❌ MISTAKE 6: Missing default in switch expression — compile error
// int i = 5;
// String r = switch (i) {
//     case 1 -> "One";
//     case 2 -> "Two";
//     // ❌ Compile error: switch expression does not cover all possible input values
// };
// ✅ Fix: add default -> "...";

// ❌ MISTAKE 7: String switch is case-sensitive
String day = "monday";
switch (day) {
    case "Monday": System.out.println("Weekday"); break;
    default: System.out.println("No match"); // This runs — 'monday' != 'Monday'
}
// ✅ Fix: normalize before switch: switch (day.substring(0,1).toUpperCase() + day.substring(1).toLowerCase())
// Or simpler: switch (day.toUpperCase()) { case "MONDAY": ... }

// ❌ MISTAKE 8: Using return in switch statement thinking it exits switch
// return inside a switch in a void method is a compile error.
// return exits the METHOD, not just the switch.
// In non-void methods it is valid — and acts like break + exits the method.

// ❌ MISTAKE 9: yield used in switch statement (not expression)
// switch (x) {
//     case 1: yield "One"; // ❌ Compile error — yield only in switch expression
// }

// ❌ MISTAKE 10: Ordering pattern cases — less specific before more specific
// Object o = 5;
// switch (o) {
//     case Integer i  -> ...;           // Catches ALL integers
//     case Integer i when i > 0 -> ...; // ❌ Unreachable — compiler warning
// }
// ✅ Fix: most specific (guarded) cases first

Bad Practices & Anti-Patterns — What Senior Developers Reject

These anti-patterns around switch usage indicate poor design decisions that make code harder to read, maintain, and extend.

🚫
Using switch for Range Conditions

Writing switch (score / 10) { case 10: ... case 9: ... } is a workaround to simulate range testing in switch. While it works for simple cases, it is fragile — what if score is negative? What if the range changes? If the condition is inherently a range (score >= 90), use if-else which expresses intent directly. Reserve switch for matching against specific discrete values, not ranges coerced into discrete form.

🚫
Classic Switch Without default

Omitting default from a classic switch statement means unmatched values silently do nothing — no error, no log, nothing. This makes runtime bugs invisible. Every classic switch should have a default case, even if it just logs an unexpected value or throws an exception. In switch expressions, the compiler enforces exhaustiveness — missing cases cause compile errors. Apply the same discipline to switch statements manually.

🚫
Intentional Fall-Through Without Comment

Writing consecutive cases without break and without a comment explaining the intention. Any reader (including future-you) will assume it is a bug and add a break, breaking your logic. If fall-through is genuinely intentional, always add: // fall through — intentional. Consider also using the @SuppressWarnings('fallthrough') annotation and a descriptive comment. Better yet, use enhanced switch comma-separated labels (case 1, 2, 3 ->) which express grouping without any fall-through risk.

🚫
String Switch Without Null Check

Switching on a String variable that could be null without a prior null check is a latent NullPointerException. In classic and Java 14 enhanced switch, null always causes NPE. Never switch on an externally-supplied String (from user input, API responses, databases) without null handling: if (s == null) { ... } before the switch, or use Java 21 case null in a pattern matching switch.

🚫
Massive Switch Statements as Dispatch Tables

A switch with 20+ cases dispatching to different behaviour based on a type or status string is a code smell — it is a procedural dispatch table masquerading as OOP. The open-closed principle says new behaviour should be added by adding code (new classes), not modifying existing switch statements. Consider replacing large type-dispatch switches with polymorphism (subclasses/interfaces), strategy pattern, or Map-based dispatch.

🚫
Mixing Arrow and Colon Labels in the Same Switch

Java does not allow mixing arrow labels (case X ->) and colon labels (case X:) in the same switch block — it is a compile error. Choose one style per switch. Arrow style is preferred for switch expressions; colon style is the classic for switch statements. When migrating from classic to enhanced switch, convert the entire switch to arrow style at once.

Real-World Production Code Examples — Switch in Context

The following examples show how switch is used in real enterprise Java and Spring Boot codebases — from enum-based state machines to pattern-matched API response handling.

☕ JavaOrderStateMachine.java — Enum Switch in a State Machine
package com.techsustainify.order;

public class OrderStateMachine {

    enum OrderStatus {
        PENDING, PAYMENT_RECEIVED, CONFIRMED, PROCESSING,
        SHIPPED, OUT_FOR_DELIVERY, DELIVERED, CANCELLED, REFUNDED
    }

    // ── Determine next valid status transitions ────────────────────────────
    public OrderStatus nextStatus(OrderStatus current) {
        return switch (current) {
            case PENDING            -> OrderStatus.PAYMENT_RECEIVED;
            case PAYMENT_RECEIVED   -> OrderStatus.CONFIRMED;
            case CONFIRMED          -> OrderStatus.PROCESSING;
            case PROCESSING         -> OrderStatus.SHIPPED;
            case SHIPPED            -> OrderStatus.OUT_FOR_DELIVERY;
            case OUT_FOR_DELIVERY   -> OrderStatus.DELIVERED;
            case DELIVERED,
                 CANCELLED,
                 REFUNDED           -> current;  // Terminal states — no next
        };
    }

    // ── Customer-facing status message ────────────────────────────────────
    public String getCustomerMessage(OrderStatus status) {
        return switch (status) {
            case PENDING            -> "We have received your order.";
            case PAYMENT_RECEIVED   -> "Payment confirmed. Processing your order.";
            case CONFIRMED          -> "Your order has been confirmed!";
            case PROCESSING         -> "Your order is being prepared.";
            case SHIPPED            -> "Your order has been shipped.";
            case OUT_FOR_DELIVERY   -> "Your order is out for delivery today!";
            case DELIVERED          -> "Your order has been delivered. Enjoy!";
            case CANCELLED          -> "Your order has been cancelled.";
            case REFUNDED           -> "Your refund has been processed.";
        };
    }

    // ── Check if cancellation is still possible ───────────────────────────
    public boolean isCancellable(OrderStatus status) {
        return switch (status) {
            case PENDING, PAYMENT_RECEIVED, CONFIRMED -> true;
            default -> false;
        };
    }

    // ── Internal action for each status ──────────────────────────────────
    public void performAction(OrderStatus status, String orderId) {
        switch (status) {
            case CONFIRMED -> {
                System.out.println("[" + orderId + "] Sending confirmation email");
                System.out.println("[" + orderId + "] Reserving inventory");
            }
            case SHIPPED -> {
                System.out.println("[" + orderId + "] Generating tracking number");
                System.out.println("[" + orderId + "] Notifying logistics partner");
            }
            case DELIVERED -> {
                System.out.println("[" + orderId + "] Sending feedback request");
                System.out.println("[" + orderId + "] Releasing payment to seller");
            }
            case CANCELLED -> {
                System.out.println("[" + orderId + "] Initiating refund");
                System.out.println("[" + orderId + "] Restoring inventory");
            }
            default -> System.out.println("[" + orderId + "] Status updated: " + status);
        }
    }
}
☕ JavaEventProcessor.java — Pattern Matching Switch for Event Handling
package com.techsustainify.event;

import java.time.LocalDateTime;

public class EventProcessor {

    // ── Sealed event hierarchy ────────────────────────────────────────────
    sealed interface AppEvent
        permits UserCreatedEvent, OrderPlacedEvent, PaymentFailedEvent,
                SystemAlertEvent {}

    record UserCreatedEvent(String userId, String email, LocalDateTime at)
        implements AppEvent {}
    record OrderPlacedEvent(String orderId, double amount, String userId)
        implements AppEvent {}
    record PaymentFailedEvent(String orderId, String reason, int attemptCount)
        implements AppEvent {}
    record SystemAlertEvent(String level, String message)
        implements AppEvent {}

    // ── Pattern matching switch for event dispatch ────────────────────────
    public void handleEvent(AppEvent event) {
        switch (event) {
            case UserCreatedEvent u -> {
                System.out.println("New user: " + u.email());
                sendWelcomeEmail(u.email());
                createUserProfile(u.userId());
            }
            case OrderPlacedEvent o when o.amount() > 10000 -> {
                System.out.println("High-value order: ₹" + o.amount());
                notifyAccountsTeam(o.orderId());
                applyLoyaltyPoints(o.userId(), o.amount());
            }
            case OrderPlacedEvent o -> {
                System.out.println("Order placed: " + o.orderId());
                processStandardOrder(o.orderId());
            }
            case PaymentFailedEvent p when p.attemptCount() >= 3 -> {
                System.out.println("Payment failed 3+ times: " + p.orderId());
                blockOrder(p.orderId());
                alertFraudTeam(p.orderId());
            }
            case PaymentFailedEvent p -> {
                System.out.println("Payment failed (attempt " + p.attemptCount() + "): " + p.reason());
                scheduleRetry(p.orderId());
            }
            case SystemAlertEvent a when a.level().equals("CRITICAL") -> {
                System.out.println("CRITICAL ALERT: " + a.message());
                pageOnCallEngineer(a.message());
            }
            case SystemAlertEvent a -> {
                System.out.println("Alert [" + a.level() + "]: " + a.message());
            }
        }
    }

    // ── Stub methods ──────────────────────────────────────────────────────
    void sendWelcomeEmail(String email)           { System.out.println("Email sent to " + email); }
    void createUserProfile(String id)             { System.out.println("Profile created: " + id); }
    void notifyAccountsTeam(String orderId)       { System.out.println("Accounts notified"); }
    void applyLoyaltyPoints(String id, double amt){ System.out.println("Points added"); }
    void processStandardOrder(String orderId)     { System.out.println("Processing: " + orderId); }
    void blockOrder(String orderId)               { System.out.println("Blocked: " + orderId); }
    void alertFraudTeam(String orderId)           { System.out.println("Fraud alert"); }
    void scheduleRetry(String orderId)            { System.out.println("Retry scheduled"); }
    void pageOnCallEngineer(String msg)           { System.out.println("Paging engineer!"); }
}

Switch Decision Flowchart — Which Form to Use

Use this decision process to choose the correct switch form for your situation.

🔀 Need multi-way branchingWhich switch form should I use?
Start
❓ Testing type of an object?instanceof-style checks needed?
YES — type patterns
✅ Pattern Matching Switchcase TypeName var -> ... (Java 21)
❓ Need the result as a value?Assign to variable, return, or use in expression?
YES — need value
✅ Switch Expression (Java 14+)String x = switch(...) { case A -> ...; };
Multiple values?
✅ Classic Switch Statementswitch(...) { case A: ...; break; }
❓ Multiple values → same result?Group them?
YES + Java 14+
✅ Comma labels (Java 14+)case 1, 2, 3 -> result;
✅ Stacked cases (classic)case 1: case 2: case 3: result; break;
❓ Multi-statement case block?More than one line in a case arm?
YES
✅ Use yield in blockcase X -> { ... yield value; }
✅ Single expressioncase X -> singleExpression;

Code Execution Flow — from source to output

Java Switch Interview Questions — Beginner to Advanced

These questions are consistently asked in Java developer interviews. Switch fall-through, switch expression, yield, and pattern matching are high-frequency topics.

Practice Questions — Test Your Java Switch Knowledge

Attempt each question independently before reading the answer — active recall is far more effective than passive reading.

1. What is the output? int x = 1; switch (x) { case 1: case 2: System.out.println("One or Two"); break; case 3: System.out.println("Three"); break; default: System.out.println("Other"); }

Easy

2. Rewrite this classic switch as an enhanced switch expression: int month = 4; int days; switch (month) { case 2: days = 28; break; case 4: case 6: case 9: case 11: days = 30; break; default: days = 31; } System.out.println(days);

Easy

3. What is the output? Explain. int day = 3; switch (day) { default: System.out.println("Default"); case 1: System.out.println("One"); break; case 3: System.out.println("Three"); break; }

Easy

4. Will this compile? If not, why? int x = 5; String result = switch (x) { case 1 -> "One"; case 2 -> "Two"; case 5 -> "Five"; };

Medium

5. What is the output and why? String s = "hello"; switch (s) { case "hello": System.out.println("Match!"); break; case "Hello": System.out.println("Capital H"); break; default: System.out.println("No match"); }

Medium

6. Write a switch expression that returns the number of days in a month. Use String month names. Throw IllegalArgumentException for invalid input.

Medium

7. What is the output? Explain yield. int x = 8; String result = switch (x) { case 10, 9 -> "A"; case 8 -> { String grade = "B"; System.out.println("Processing grade B"); yield grade; } default -> "C or below"; }; System.out.println("Result: " + result);

Medium

8. Write a pattern matching switch (Java 21) that processes different event types: UserEvent(String userId), OrderEvent(String orderId, double amount), SystemEvent(String level). For OrderEvent with amount > 5000, print 'HIGH VALUE'. For SystemEvent with level CRITICAL, print 'ALERT'. Handle null.

Hard

Conclusion — Switch: From Simple Dispatch to Powerful Pattern Matching

Java's switch has evolved from a simple multi-way branch with fall-through quirks into one of the most expressive features of modern Java. The classic switch statement is reliable for basic branching — but requires discipline with break. The enhanced switch expression (Java 14+) eliminates fall-through, produces values, and enforces exhaustiveness — making it the preferred form for most switching needs. The pattern matching switch (Java 21) brings type-safe dispatch, guarded conditions, and null handling into a single, concise construct.

Professional Java code favours switch expressions over classic switch statements for their safety and conciseness. It uses enum + switch expression for state machines, pattern matching switch for type-based dispatch over sealed hierarchies, and if-else when ranges or compound conditions are needed. Understanding when to use which form — and the pitfalls of fall-through and null — is a mark of Java mastery.

ConceptTool / SyntaxKey Rule
Classic switch statementswitch(x) { case A: ... break; }Needs break. Fall-through possible.
Enhanced switch expressionvar r = switch(x) { case A -> ...; };Java 14+. No fall-through. Produces value.
Arrow labelcase X -> expressionNo fall-through. Concise.
Block armcase X -> { ... yield value; }Multi-statement. yield provides value.
Colon + yieldcase X: yield value;Traditional label in switch expression.
yield keywordyield value;Produces switch expression value. Not return.
default casedefault -> ...Required in switch expression. Recommended always.
Comma-separated labelscase 1, 2, 3 -> ...Java 14+. Replaces stacked cases.
Fall-throughMissing break in classic switchUsually a bug. Document if intentional.
String switchswitch(str) — Java 7+Case-sensitive. Null causes NPE.
Enum switchswitch(enumVal) — all versionsExhaustive. No default needed if all covered.
Pattern matching switchcase TypeName var -> (Java 21)Type patterns. Replaces instanceof chains.
Guarded patterncase Type t when condition -> ...Java 21. Most specific cases first.
Null in switchcase null -> ... (Java 21)Only in pattern matching switch.
Unsupported typeslong, float, double, booleanNot allowed as switch selector.

Your next step: Java for Loop — where you will use switch alongside loops to build complete iteration and control flow logic for real programs. ☕

Frequently Asked Questions — Java Switch