Pattern matching for switch in Java
case Circle c ->, guards with when, null cases.
Switch on types
Since Java 21, switch can match on types. case Integer i -> runs when the value is an Integer and binds it to i, ready to use. Type test, cast and variable, all in one step.
String describe(Object o) {
return switch (o) {
case Integer i -> "int " + i;
case String s -> "text " + s;
default -> "other";
};
}Guards with when
Refine a case with a guard: case String s when s.isBlank() ->. It matches only if the type test succeeds and the guard is true. Cases are tried top to bottom; if a guard fails, the later cases still get their chance.
return switch (o) {
case String s when s.isBlank() -> "blank";
case String s -> "text";
default -> "other";
};Your turn
What does this print?
Object[] items = {5, 12, "", 2.5};
for (Object o : items) {
String r = switch (o) {
case Integer i when i > 9 -> "B";
case Integer i -> "S";
case String s when s.isEmpty() -> "E";
default -> "?";
};
System.out.print(r + " ");
}S B E ?B B E ?S B ? ?
Show the answer
5 fails the i > 9 guard and falls to the plain Integer case: S. 12 passes: B. "" is an empty String: E. 2.5 is a Double, which reaches default: ?.
default doesn't catch null
default does not match null. A switch with no case null throws NullPointerException when the selector is null. Handle it explicitly, or combine both into one label: case null, default ->.
return switch (o) {
case null -> "null!";
case String s -> "text";
default -> "other";
};
// or: case null, default -> "other";Order matters
Every Integer is a Number. What happens?
Object o = 7;
String r = switch (o) {
case Number n -> "number";
case Integer i -> "int";
default -> "other";
};
System.out.println(r);numberintCompile error
Show the answer
Compile error. The Number case would catch every Integer, so the Integer case could never be reached. The compiler reports it as dominated. Put specific cases before general ones.
Goodbye, instanceof chains
Event handlers, message routers and JSON-node walkers used to be long if/else instanceof ladders with casts, which made it easy to forget a branch. A pattern switch states every case side by side, and the compiler catches unreachable ones. It's one of the first things teams adopt after moving to Java 21.
Key takeaways
- case Type name -> tests the type and binds in one step
- Guards refine a case: case String s when s.isEmpty() ->
- Without case null, a null selector throws NullPointerException
- Specific cases must come before general ones (dominance)
Pattern matching for switch went through four preview rounds (Java 17 to 20) before becoming final in Java 21.
Practice questions
What does this print?
Object[] items = {42, "hi", 3.5, 7};
for (Object o : items) {
String r = switch (o) {
case Integer i when i > 10 -> "big";
case Integer i -> "small";
case String s -> "str";
default -> "other";
};
System.out.print(r + " ");
}- big str other small
- big str other big
- small str other small
- big str str small
Check your answer
big str other small. 42 passes the guard (big), "hi" is a String, 3.5 is a Double so it reaches default, and 7 fails the guard and falls to the plain Integer case.
What does this print?
Object o = null;
String r = switch (o) {
case String s -> "string";
default -> "other";
};
System.out.println(r);- Throws NullPointerException
- other
- string
- null
Check your answer
Throws NullPointerException. default does not match null. A switch without case null throws NullPointerException when the selector is null.