JAVA / BRANCHING AND SWITCH EXPRESSIONS
Arrow cases and block-scoped switch branches
Convert colon-style cases into arrow branches, share one body across several labels, and use braces to give each branch its own variable scope.
What you will learn
- Give one body to several labels with `case A, B ->` instead of stacked labels
- Wrap a multi-statement arrow branch in braces; an expression or throw needs none
- Reuse a local name across arrow branches, since each braced branch is its own scope
- Declare before the switch any variable the code after the switch must read
Understanding Arrow cases and block-scoped switch branches
`case 200 ->` reads as a mapping: labels on the left, exactly one action on the right. The grammar allows only three things after the arrow: an expression statement such as a method call, a `throw`, or a block in braces. That restriction is what makes arrow branches self-contained, because there is no way to write a statement that sits in the switch body but belongs to no particular label, which is precisely the opening a colon label gives execution to run on into the next label's statements.
With colon labels the entire switch body is one block, so a `String detail = ...;` written under `case 301:` is still in scope under `case 404:`; you cannot reuse the name there, and the compiler rejects reading it because it is not definitely assigned on that path. Braces after an arrow are an ordinary block, so each branch gets its own scope and `detail` in one branch has nothing to do with `detail` in another. The mental model is that a braced arrow branch behaves like a tiny method body: control arrives, it uses its own locals, and its names cease to exist at the closing brace.
Several labels can share one arrow body by listing them with commas, as in `case 301, 302 ->`, which is the arrow form's replacement for stacking labels above a shared statement group. Because a branch's locals vanish at its closing brace, any value that later code needs must live in a variable declared before the switch, or be produced by a switch expression, which is a separate topic. A single switch cannot mix the two styles: putting `case 1 ->` next to `case 2:` is rejected with "different case kinds used in the switch", so conversion is all-or-nothing per switch.
public class ArrowBranches {
static void report(int status) {
switch (status) {
case 200 -> System.out.println("200 ok");
case 301, 302 -> {
String detail = "redirect"; // scoped to this branch only
System.out.println(status + " " + detail);
}
case 404 -> {
String detail = "no such resource"; // same name, different block
System.out.println(status + " " + detail);
}
default -> System.out.println(status + " unclassified");
}
}
public static void main(String[] args) {
report(200);
report(301);
report(302);
report(404);
report(503);
}
}An arrow case attaches exactly one action to its labels, and when that action is a braced block, the block is a scope of its own.
Worked examples
One shared scope in the colon form
Shows why the colon form lets a later case assign a variable declared under an earlier case, the leak that arrow braces remove.
public class SharedScope {
public static void main(String[] args) {
int day = 7;
switch (day) {
case 6:
String label = "weekend";
System.out.println("6 " + label);
break;
case 7:
label = "weekend too";
System.out.println("7 " + label);
break;
default:
System.out.println("weekday");
}
}
}Example explained
Line 1`String label` is written under `case 6:` but its scope is the whole switch body, so `case 7:` can assign to the same variable.
Line 2With day 7 the initializer `= "weekend"` never runs; only the assignment in `case 7:` does, so the later read is definitely assigned and compiles.
Line 3Rewriting these as `case 6 -> { ... }` and `case 7 -> { ... }` stops this working: each branch must then declare its own `String label`.
The three legal arrow bodies
Demonstrates an expression statement, a braced block with branch-local variables, and a throw as the body of an arrow case.
public class ArrowBodies {
static void handle(String command) {
switch (command) {
case "ping" -> System.out.println("pong");
case "sum" -> {
int total = 0;
for (int i = 1; i <= 4; i++) {
total += i;
}
System.out.println("sum=" + total);
}
default -> throw new IllegalArgumentException("unknown command: " + command);
}
}
public static void main(String[] args) {
handle("ping");
handle("sum");
try {
handle("dance");
} catch (IllegalArgumentException e) {
System.out.println("caught: " + e.getMessage());
}
}
}Example explained
Line 1The `"ping"` branch is a single expression statement, so braces would add nothing.
Line 2The `"sum"` branch needs a counter and a loop, so it must be a block; `total` and `i` exist only until that closing brace.
Line 3`default -> throw ...` is the third permitted body, and a throw needs no braces around it.
Line 4On the "dance" call control leaves `handle` by throwing instead of returning, which is why `main` prints from its catch block.
Important notes
Without braces the arrow accepts only an expression statement or a `throw`; `case 1 -> if (ok) f();` and `case 1 -> int n = 2;` are syntax errors, so those need a block.
Arrow labels are standard from Java 14 onwards; compiling with an older `--source`/`--release` reports that arrow labels are not supported.
Common mistakes
Mixing styles inside one switch, such as `case 1 ->` beside `case 2:`, fails to compile with "different case kinds used in the switch"; every label in that switch has to use the same style.
Writing a second statement after an arrow without braces, as in `case 1 -> f(); g();`, does not attach `g()` to the branch: an arrow switch body accepts only rules there, so it is a syntax error ("case, default, or '}' expected").
Declaring the result inside a branch, as in `case 1 -> { int size = 10; }`, and then printing `size` after the switch gives "cannot find symbol", because the name ended at the branch's closing brace.
Try it yourself
Change, predict, then run
Write `static void grade(int score)` whose arrow switch has the branches `case 10, 9 ->`, `case 8, 7 ->` and `default ->`, where each braced branch declares its own `String label` and prints it, then call it with 10, 7 and 4. Next move one `label` declaration above the switch and see which reads still compile.
Open the Java workspaceCheck your understanding
A colon-form switch declares `int size` under its first case and assigns it under a later case, and it compiles. After every label is converted to an arrow with braces, the assignment in the later branch no longer compiles. What changed?
- Arrow branches run in their own stack frame, so locals cannot be shared between them
- The arrow form makes locals declared inside a branch implicitly final
- Each braced arrow branch is a separate block, so a name declared in one branch is out of scope in the others
- A switch using arrow labels must declare all of its locals before the first label
Show answer
Braces after an arrow open an ordinary block, and declarations inside it end at the matching closing brace, whereas the colon form has a single block spanning the whole switch body, which is why the shared name worked there. The stack frame answer is wrong because both forms execute in the same method invocation and the same frame; scope is decided by the compiler from the braces, not by anything happening at runtime.