switch Statements & Expressions

Java · Lesson 10 of 32 7 min read Updated

When one value can lead to many different actions, a long if/else if chain becomes hard to read. The switch statement compares one value against a list of cases and jumps straight to the matching one.

Java's switch has changed a lot. The classic form with case ... : and break dates back to Java 1.0 and is still everywhere in existing code and exam papers. Modern Java (14 and later) adds the cleaner arrow form and switch expressions, and Java 21 adds pattern matching. In this lesson you will learn all three, starting with the old form so you can read any code you meet.

The classic switch statement

A classic switch checks a value against each case label. When one matches, Java runs the statements from that point onwards until it reaches break. The default label handles every value that no case matched.

Java
public class OldSwitch {
    public static void main(String[] args) {
        int day = 3;
        String name;

        switch (day) {
            case 1:
                name = "Monday";
                break;
            case 2:
                name = "Tuesday";
                break;
            case 3:
                name = "Wednesday";
                break;
            default:
                name = "Some other day";
                break;
        }
        System.out.println(name);
    }
}
Output
Wednesday

You can switch on int, char, byte, short, their wrapper classes, String (since Java 7) and enums. You cannot switch on double, float, long or boolean in a classic switch; the compiler reports error: selector type double is not allowed.

Fall-through

If you forget break, Java does not stop at the end of a case. It falls through into the next case and keeps running statements. Occasionally this is useful, as in this credit-card example where higher plans also get every lower plan's benefits:

Java
public class FallThrough {
    public static void main(String[] args) {
        String plan = "SILVER";

        System.out.println("Benefits of " + plan + ":");
        switch (plan) {
            case "GOLD":
                System.out.println("- Airport lounge access");
            case "SILVER":
                System.out.println("- Free movie tickets");
            case "BASIC":
                System.out.println("- Fuel surcharge waiver");
                break;
            default:
                System.out.println("- Unknown plan");
        }
    }
}
Output
Benefits of SILVER:
- Free movie tickets
- Fuel surcharge waiver

Much more often, fall-through is an accident: someone forgets a break and two cases run. This bug was so common that modern Java added a form without it.

Arrow labels and switch expressions

Since Java 14, you can write case X -> instead of case X:. An arrow case runs only the code to the right of the arrow: there is no fall-through, and no break is needed. One case can list several values separated by commas. Use braces when a case needs more than one statement.

Even better, switch can be an expression that produces a value, which you assign directly to a variable.

Java
public class Arrow {
    public static void main(String[] args) {
        String day = "SAT";

        switch (day) {
            case "SAT", "SUN" -> System.out.println("Weekend: shop opens at 10");
            case "FRI" -> {
                System.out.println("Friday: shop opens at 9");
                System.out.println("Late closing at 10 pm");
            }
            default -> System.out.println("Weekday: shop opens at 9");
        }

        int month = 2;
        int days = switch (month) {
            case 4, 6, 9, 11 -> 30;
            case 2 -> 28;
            default -> 31;
        };
        System.out.println("Days in month " + month + ": " + days);
    }
}
Output
Weekend: shop opens at 10
Days in month 2: 28

Note the semicolon after the closing brace of the switch expression: the whole thing is one assignment statement. A switch expression must be exhaustive, meaning it must produce a value for every possible input. For an int or a String that means you need a default; without it the compiler says error: the switch expression does not cover all possible input values. This check is a big safety improvement over the old style, where a missing case silently did nothing.

yield: returning a value from a block

When a case in a switch expression needs several statements, put them in braces and give the result with yield:

Java
public class Yield {
    public static void main(String[] args) {
        char grade = 'B';
        int marks = 81;

        String remark = switch (grade) {
            case 'A' -> "Excellent";
            case 'B' -> {
                if (marks >= 80) {
                    yield "Very good, almost an A";
                }
                yield "Very good";
            }
            case 'C' -> "Good";
            default -> "Keep practising";
        };
        System.out.println(remark);
    }
}
Output
Very good, almost an A

yield is like return, but it only leaves the switch, not the whole method.

Pattern matching in switch

Since Java 21, a case can test the type of a value, not just compare it with a constant. This is called a type pattern. case Integer n means "if the value is an Integer, call it n and run this case". You can add a condition after when (called a guard), and you can handle null with case null.

Java
public class Patterns {
    static String describe(Object value) {
        return switch (value) {
            case null -> "nothing at all";
            case Integer n when n > 1000 -> "a big number: " + n;
            case Integer n -> "a number: " + n;
            case String s when s.isBlank() -> "an empty-looking string";
            case String s -> "text with " + s.length() + " letters";
            case Double d -> "a decimal: " + d;
            default -> "something else: " + value.getClass().getSimpleName();
        };
    }

    public static void main(String[] args) {
        System.out.println(describe(42));
        System.out.println(describe(250000));
        System.out.println(describe("Ananya"));
        System.out.println(describe("   "));
        System.out.println(describe(99.5));
        System.out.println(describe(null));
        System.out.println(describe('Z'));
    }
}
Output
a number: 42
a big number: 250000
text with 6 letters
an empty-looking string
a decimal: 99.5
nothing at all
something else: Character

Object is a type that can hold any value; static String describe(...) is a method, which you will study in Methods & Overloading. The cases are checked from top to bottom, so the guarded Integer n when n > 1000 must come before the plain Integer n. If you swap them, the compiler reports error: this case label is dominated by a preceding case label, because the guarded case could never be reached.

Without case null, a switch on a null value throws a NullPointerException, which is also what every classic switch does. Pattern matching really shines with records and sealed classes, where the compiler can prove a switch covers every possible type without a default. You will see that in Sealed Classes & Pattern Matching.

Choosing between if and switch

  • Use switch when you compare one value against several fixed values or types.
  • Use if/else if for ranges (marks >= 75) and conditions that involve several variables.
  • Prefer the arrow form for new code. Use switch expressions whenever each case produces a value.

Common mistakes

  • Forgetting break in a classic switch. The next case runs too. The arrow form removes this risk.
  • Mixing : and -> in one switch. A switch must use one style throughout.
  • Missing cases in a switch expression. Add a default, or cover every enum constant or sealed subtype.
  • Switching on null. Without case null, Java throws NullPointerException.
  • Putting a general pattern before a specific one. case Integer n before case Integer n when n > 1000 makes the second unreachable, and the compiler reports that it is dominated.

Key points

  • A classic switch uses case value: and needs break to stop fall-through.
  • Arrow cases (case value ->) never fall through and can list several values.
  • A switch expression returns a value, must be exhaustive, and uses yield inside blocks.
  • Since Java 21, cases can match types (case String s), add guards with when, and handle case null.
  • Java 22 lets you write _ for a pattern variable you do not use.

Exercise

Write Recharge.java with a switch expression that turns a plan code into a description: "P199" and "P239" are "Basic monthly plan", "P599" is "Quarterly plan", "P2999" is "Annual plan", and anything else is "Unknown plan". Then write a second switch expression using a block and yield that returns the validity in days (28, 28, 84, 365) and, for unknown codes, prints a warning and yields 0. Test it with "P599" and "P100".

Show solution
Java
public class Recharge {
    public static void main(String[] args) {
        String[] codes = {"P599", "P100"};
        for (String code : codes) {
            String plan = switch (code) {
                case "P199", "P239" -> "Basic monthly plan";
                case "P599" -> "Quarterly plan";
                case "P2999" -> "Annual plan";
                default -> "Unknown plan";
            };

            int validity = switch (code) {
                case "P199", "P239" -> 28;
                case "P599" -> 84;
                case "P2999" -> 365;
                default -> {
                    System.out.println("Warning: no such plan " + code);
                    yield 0;
                }
            };
            System.out.println(code + ": " + plan + ", " + validity + " days");
        }
    }
}
Output
P599: Quarterly plan, 84 days
Warning: no such plan P100
P100: Unknown plan, 0 days

Both switches are expressions, so each needs a default to be exhaustive. The default block uses yield because it runs a statement before producing its value.

Quick Quiz

1. What happens in a classic switch when a matching case has no break?

2. Which keyword gives the value of a block inside a switch expression?

3. Why does int days = switch (month) { case 2 -> 28; case 4 -> 30; }; fail to compile?

4. In Java 21, what does case String s when s.isBlank() -> do?

Learn Java with a mentor

Free lessons get you started. Our Java, C & C++ Programming training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call