Switch Statement & Switch Expressions

Reviewed & published by Brayan K

When you need to pick one path out of many based on a single value, a chain of if-else gets noisy fast. switch says it once, cleanly. This lesson covers the classic statement, the modern arrow-form expression, fall-through, and yield.

Part of the free Java course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

Before You Start

You should be comfortable with if-else conditionals and Enums — switch pairs beautifully with enums. The modern arrow form requires Java 14 or newer (which is everything from 2020 onward).

What You'll Learn

The Big Idea — A Telephone Switchboard

💡 Analogy: A switch is an old telephone switchboard operator. You give one input — "extension 3" — and the operator connects you to exactly one line. A chain of if-else is like a security guard checking a list top to bottom, asking "is it 1? is it 2? is it 3?" over and over. The switchboard jumps straight to the right line.

There are two flavours. The classic statement (with case ... : and break) has been in Java forever and runs side effects. The switch expression (with case ... ->) is newer, returns a value, and removes the fall-through trap. We'll learn both, but you should reach for the arrow form in new code.

1️⃣ The Classic switch Statement

The original form evaluates a value once, then jumps to the matching case label. Each branch ends with break to stop execution from sliding into the next case. default is the catch-all when nothing matched.

switch (value) {
    case A:
        // do something
        break;
    case B:
        // do something else
        break;
    default:
        // fallback
}

Notice two cases can share a block by stacking labels (case 6: case 7:). Read the worked example, then run it.

public class Main {
    public static void main(String[] args) {
        int dayNumber = 3;
        String dayName;

        // The CLASSIC switch statement (works in every Java version)
        switch (dayNumber) {
            case 1:
                dayName = "Monday";
                break;            // break stops fall-through
            case 2:
                dayName = "Tuesday";
                break;
            case 3:
                dayName = "Wednesday";
                break;
            case 4:
                dayName = "Thursday";
                break;
            case 5:
                dayName = "Friday";
                break;
            case 6:
            case 7:               // two labels share one block
                dayName = "Weekend";
                break;
            default:              // runs when nothing else matched
                dayName = "Unknown";
        }

        System.out.println("Day " + dayNumber + " is " + dayName);
    }
}

2️⃣ Fall-Through — the Classic Trap

In the classic form, once a case matches, execution keeps running through every following case until it hits a break. Forget the break and you get extra, unintended output. This is the single most common switch bug. The example below omits every break on purpose so you can see it happen — execution enters at case 2 and tumbles all the way down.

public class Main {
    public static void main(String[] args) {
        int level = 2;

        // FORGOT the break statements on purpose — watch fall-through happen
        System.out.println("Permissions for level " + level + ":");
        switch (level) {
            case 1:
                System.out.println("- read");
                // no break! execution falls into the next case
            case 2:
                System.out.println("- write");
                // no break! falls again
            case 3:
                System.out.println("- delete");
                // no break! falls again
            default:
                System.out.println("- admin");
        }
    }
}

3️⃣ The Modern switch Expression (Java 14+)

The arrow form fixes everything awkward about the classic switch:

String season = switch (month) {
    case 12, 1, 2 -> "Winter";
    case 3, 4, 5  -> "Spring";
    default       -> "Other";
};   // note the semicolon — it's an expression assigned to a variable
public class Main {
    public static void main(String[] args) {
        int month = 4;

        // SWITCH EXPRESSION (Java 14+) — the arrow form RETURNS a value.
        // No break needed, no fall-through, and it must be exhaustive.
        String season = switch (month) {
            case 12, 1, 2 -> "Winter";
            case 3, 4, 5  -> "Spring";
            case 6, 7, 8  -> "Summer";
            case 9, 10, 11 -> "Autumn";
            default -> "Invalid month";
        };

        System.out.println("Month " + month + " is in " + season);

        // It's an expression, so you can use it directly in other code:
        int daysInQuarter = switch (season) {
            case "Winter" -> 90;
            case "Spring" -> 92;
            case "Summer" -> 92;
            case "Autumn" -> 91;
            default -> 0;
        };
        System.out.println(season + " has about " + daysInQuarter + " days");
    }
}

4️⃣ Multi-Line Branches with yield

A single-expression branch (case PENNY -> 1;) returns its value automatically. But when a branch needs several statements, wrap them in { } and end with yield to hand back the result. Switching over an enum is special: if you cover every constant, you don't even need a default — the compiler proves the switch is exhaustive.

public class Main {
    enum Coin { PENNY, NICKEL, DIME, QUARTER }

    public static void main(String[] args) {
        Coin coin = Coin.QUARTER;

        // When a branch needs more than one line, use a { block } and 'yield'
        // to hand back the value. switch over an enum needs no 'default' if
        // every constant is covered.
        int cents = switch (coin) {
            case PENNY   -> 1;
            case NICKEL  -> 5;
            case DIME    -> 10;
            case QUARTER -> {
                System.out.println("(calculating the big one...)");
                int value = 25;
                yield value;          // yield returns from a block branch
            }
        };

        System.out.println(coin + " is worth " + cents + " cents");
    }
}

5️⃣ Switching on Strings

You can switch on a String — handy for parsing commands or menu input. Two things to remember: matching is case-sensitive ("stop" ≠ "STOP"), and switching on a null String throws a NullPointerException, so guard against null first.

public class Main {
    public static void main(String[] args) {
        String command = "stop";

        String response = switch (command) {
            case "start" -> "Engine started.";
            case "stop"  -> "Engine stopped.";
            case "pause" -> "Engine paused.";
            default      -> "Unknown command: " + command;
        };

        System.out.println(response);

        // switch on String is case-SENSITIVE
        String upper = "STOP";
        String result = switch (upper) {
            case "stop" -> "matched";
            default     -> "no match (case matters!)";
        };
        System.out.println("\"" + upper + "\" -> " + result);
    }
}

🧩 Reorder Challenge

These lines build a switch expression that turns a traffic-light colour into an action, then prints it. They've been shuffled. Put them in the right order in your head, then check.

A)     };
B)         case "RED" -> "Stop";
C) String light = "AMBER";
D)     System.out.println(light + " -> " + action);
E) String action = switch (light) {
F)         default -> "Unknown";
G)         case "GREEN" -> "Go";
H)         case "AMBER" -> "Slow down";

Correct order: C, E, B, G, H, F, A, D

String light = "AMBER";
String action = switch (light) {
    case "RED" -> "Stop";
    case "GREEN" -> "Go";
    case "AMBER" -> "Slow down";
    default -> "Unknown";
};
System.out.println(light + " -> " + action);
// Output:  AMBER -> Slow down

Declare the input first, open the switch, list the cases, close it with }; (note the semicolon — it's an expression), then print.

🧠 Quick Recall

Predict the output before opening each answer.

int n = 2;
switch (n) {
    case 1: System.out.println("one");
    case 2: System.out.println("two");
    case 3: System.out.println("three"); break;
    case 4: System.out.println("four");
}

Answer: two then three. Matching enters at case 2, prints "two", and — with no break — falls through into case 3, prints "three", then hits break and stops. case 4 never runs.

String tshirt = "L";
int size = switch (tshirt) {
    case "S" -> 1;
    case "M" -> 2;
    case "L" -> 3;
    default  -> 0;
};
System.out.println(size);

Answer: 3. The arrow form runs only the matching branch and returns its value, which is assigned straight to size. No break, no fall-through.

int x = 5;
String s = switch (x) {
    case 5 -> {
        System.out.println("computing");
        yield "five";
    }
    default -> "other";
};
System.out.println(s);

Answer: computing then five. The block branch runs the println side effect, then yield "five" hands the value back, which is printed on the next line.

🎯 Your Turn #1 — Modernise a switch

Convert the classic colon-form switch into a switch expression using the arrow form. Run it and confirm the output.

public class Main {
    public static void main(String[] args) {
        char grade = 'B';

        // YOUR TURN: convert this classic switch into a switch EXPRESSION
        // that assigns a message to 'feedback'. Use the arrow (->) form.
        // Expected output:  Grade B: Good work!
        String feedback;
        switch (grade) {
            case 'A': feedback = "Grade A: Excellent!"; break;
            case 'B': feedback = "Grade B: Good work!"; break;
            case 'C': feedback = "Grade C: Keep practising"; break;
            default:  feedback = "Grade " + grade + ": See me";
        }

        System.out.println(feedback);
    }
}

🎯 Your Turn #2 — Categorise HTTP codes

Write a switch expression that maps a status code to a category, grouping several codes per branch.

public class Main {
    public static void main(String[] args) {
        int httpStatus = 404;

        // YOUR TURN: write a switch EXPRESSION that maps the status code to
        // a category String. Group 200,201 -> "Success"; 301,302 -> "Redirect";
        // 400,401,403,404 -> "Client Error"; default -> "Other".
        // Expected output:  404 -> Client Error
        String category = "";   // replace with your switch expression

        System.out.println(httpStatus + " -> " + category);
    }
}

🧩 Mini-Challenge — Tiny calculator

Only a comment outline is given. Build a calculate method that switches on the operator and guards division by zero with a yield block.

public class Main {
    // GOAL: a tiny calculator using a switch expression.
    // Read the comments — write the body yourself.
    //
    // Expected output:
    //   7 + 3 = 10
    //   7 - 3 = 4
    //   7 * 3 = 21
    //   7 / 0 = ERROR: divide by zero

    static String calculate(int a, char op, int b) {
        // 1. switch on 'op' with cases '+', '-', '*', '/'
        // 2. for '/', guard against b == 0 using a { block } and yield
        // 3. default -> return "ERROR: unknown operator"
        // 4. return a formatted line like  "7 + 3 = 10"
        return "";   // your code here
    }

    public static void main(String[] args) {
        System.out.println(calculate(7, '+', 3));
        System.out.println(calculate(7, '-', 3));
        System.out.println(calculate(7, '*', 3));
        System.out.println(calculate(7, '/', 0));
    }
}

Common Errors

Pro Tips

📋 Quick Reference

TaskSyntaxNotes
Classic casecase 1: ...; break;break stops fall-through
Catch-alldefault: ...Runs when nothing matched
Arrow branchcase 1 -> "one";No break, returns a value
Multiple labelscase 1, 2, 3 -> ...One branch, several values
Multi-line branchcase X -> { ... yield v; }yield returns the value
Assign a switchvar r = switch(x) { ... };Ends with brace + semicolon

🎉 Lesson Complete!

You can now write both forms of switch: the classic statement with break and default, and the modern arrow-form expression that returns a value with no fall-through. You understand yield, exhaustive enum switches, and the case-sensitivity of String switches.

Practice quiz

In the classic colon-form switch, what stops execution from falling into the next case?

  • continue
  • yield
  • break
  • return

Answer: break. A break statement ends the case; without it, execution falls through.

Consider: int n=2; switch(n){ case 1: print('one'); case 2: print('two'); case 3: print('three'); break; case 4: print('four'); } — what prints?

  • two three
  • two
  • one two three
  • three

Answer: two three. It enters at case 2, prints 'two', falls into case 3 printing 'three', then break stops it.

Which symbol marks the modern arrow-form switch branch (Java 14+)?

  • : (colon)
  • => (fat arrow)
  • | (pipe)
  • -> (arrow)

Answer: -> (arrow). The arrow form uses 'case X -> ...' with no fall-through.

What is true of the arrow-form switch?

  • It needs break statements
  • Only the matching branch runs, with no fall-through
  • It has fall-through
  • It cannot return a value

Answer: Only the matching branch runs, with no fall-through. The arrow form runs only the matching branch and has no fall-through.

Inside a multi-line block branch of a switch expression, how do you return the value?

  • yield value;
  • return value;
  • break value;
  • give value;

Answer: yield value;. yield hands a value back from a { } block branch of a switch expression.

A switch expression assigned to a variable must end with...

  • just a brace }
  • the word end
  • a brace and a semicolon };
  • a colon

Answer: a brace and a semicolon };. It is an expression assigned to a variable, so it ends with } and a semicolon.

When switching over an enum and covering every constant, do you need a default?

  • Yes, always
  • No — the compiler proves it is exhaustive
  • Only in the colon form
  • Only with yield

Answer: No — the compiler proves it is exhaustive. An enum switch covering all constants is exhaustive, so no default is required.

Switching on the String 'STOP' against case "stop" — does it match?

  • Yes
  • Only with ignoreCase
  • It throws
  • No, matching is case-sensitive

Answer: No, matching is case-sensitive. String switch matching is case-sensitive, so 'STOP' does not match 'stop'.

Which of these types CANNOT be used in a switch?

  • int
  • double
  • String
  • enum

Answer: double. You cannot switch on double (or long, float, boolean); int, String, char, and enum are allowed.

What does an arrow switch expression do that the classic statement does not?

  • Run side effects
  • Use case labels
  • Return a value you can assign
  • Have a default

Answer: Return a value you can assign. The arrow form is an expression that returns a value you can assign directly.

Continue this course

Frequently asked questions

When should I use a switch instead of if-else?

Use a switch when you are comparing one variable against several constant values — like a status code, an enum, or a menu choice. It reads more clearly than a long if-else chain and the compiler can check an enum switch is exhaustive. Keep if-else for ranges and complex boolean conditions (e.g. age > 18 && hasLicence), which a switch cannot express.

What is the difference between the colon (:) form and the arrow (->) form?

The colon form is the classic statement: each case needs an explicit break or it falls through to the next case, and it doesn't return a value. The arrow form (Java 14+) runs only the matching branch with no fall-through, no break needed, and can be used as an expression that returns a value. Prefer the arrow form in new code — it removes the single biggest source of switch bugs.

What does 'yield' do?

yield returns a value from a switch-expression branch that uses a { } block. The single-expression arrow form (case 1 -> 5;) returns automatically, but when a branch needs several statements you wrap them in braces and end with yield value; to hand the result back. yield is to a switch expression what return is to a method.

Why does my switch run more cases than I expected?

That's fall-through. In the classic colon form, once a case matches, execution continues into the following cases until it hits a break or the end of the switch. If you forget break, you get unexpected extra output. The arrow form has no fall-through at all, which is why it is recommended for new code.

What types can I switch on?

You can switch on byte, short, char, int (and their wrapper classes), String, and enum constants. You cannot switch on long, float, double, or boolean. For an enum you don't need a default if every constant is handled — the compiler verifies it. For String, matching is case-sensitive and the value must not be null (a null throws a NullPointerException).

Related lessons