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
- Write a classic switch statement with case, break, and default
- Understand fall-through and why a missing break causes bugs
- Use the modern arrow-form switch expression (Java 14+)
- Return a value from a switch and assign it directly
- Use yield for multi-line switch branches
- Switch on int, char, String, and enum values
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:
- No break needed — only the matching branch runs, then it stops. No fall-through.
- It returns a value — you can assign the whole switch to a variable.
- One label can list multiple values — case 12, 1, 2 -> ....
- It must be exhaustive — cover every case, or include a default, or it won't compile.
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 variablepublic 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 downDeclare 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
- ❌ Forgetting break in the classic form. Output from several cases appears instead of one. Fix: add break at the end of each case, or switch to the arrow form which has no fall-through.
- ❌ Missing the semicolon on a switch expression. error: ';' expected after the closing brace. A switch expression is assigned to a variable, so it ends with }; — brace and semicolon.
- ❌ Non-exhaustive switch expression. error: the switch expression does not cover all possible input values. Fix: add a default -> branch (or, for enums, cover every constant).
- ❌ Using return instead of yield in a block branch. return would exit the whole method. Fix: use yield value; to return a value from the switch.
- ❌ Switching on a null String. Throws NullPointerException. Fix: check if (s != null) first, or handle null before the switch.
- ❌ Trying to switch on a double or boolean. Not allowed. Switch supports byte, short, char, int, String, and enum only.
Pro Tips
- 💡 Default to the arrow form in new code. It's shorter, safer, and reads as a clean mapping from input to output.
- 💡 Switch on enums, not magic strings, when you can. The compiler then checks you've handled every constant.
- 💡 Keep branches tiny. If a branch grows past a couple of lines, extract a method and call it from the branch.
- 💡 An arrow switch with no default over a complete enum is a feature, not a gap — it forces you to update the switch when you add an enum constant.
📋 Quick Reference
| Task | Syntax | Notes |
|---|---|---|
| Classic case | case 1: ...; break; | break stops fall-through |
| Catch-all | default: ... | Runs when nothing matched |
| Arrow branch | case 1 -> "one"; | No break, returns a value |
| Multiple labels | case 1, 2, 3 -> ... | One branch, several values |
| Multi-line branch | case X -> { ... yield v; } | yield returns the value |
| Assign a switch | var 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
- Previous: Enums
- Next: Wrapper Classes & Autoboxing — Integer, Double, Boolean — and how autoboxing works
- Quick reference: Java cheat sheet
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).