Smarter Switch Statements

Pattern Matching Switch

in Java 21 with Pattern Matching

Java has come a long way since the days of switch (int) and switch (String). With Java 21, the switch statement becomes way more expressive thanks to pattern matching — allowing it to work seamlessly with types, records, sealed hierarchies, and more.

In this post, we’ll explore what’s new in switch, how it improves readability, safety, and power, and see it in action with real examples.


Why Pattern Matching in Switch?

Traditionally, handling polymorphic logic in Java looked something like this:

if (obj instanceof String s) {
    System.out.println(s.toUpperCase());
} else if (obj instanceof Integer i) {
    System.out.println(i + 1);
}

Now with pattern matching in switch, you get a cleaner, more concise version:

Object obj = "Hello";

switch (obj) {
    case String s -> System.out.println("String: " + s.toUpperCase());
    case Integer i -> System.out.println("Integer + 1: " + (i + 1));
    case null -> System.out.println("It's null!");
    default -> System.out.println("Unknown type");
}

Guards with when Clauses

Pattern matching also supports guard conditions using when:

Object obj = 42;

switch (obj) {
    case Integer i when i > 100 -> System.out.println("Large integer: " + i);
    case Integer i -> System.out.println("Small integer: " + i);
    default -> System.out.println("Other type");
}

This allows more nuanced branching without breaking type safety or resorting to deeply nested if statements.


Records + Sealed Interfaces: A Perfect Match

Let’s bring in some more modern Java tools — records and sealed interfaces — to see the real power of pattern matching.

1. Define a Sealed Interface

sealed interface Notification permits Email, SMS {}

record Email(String from, String subject, String body) implements Notification {}
record SMS(String number, String message) implements Notification {}

2. Handle Logic with Pattern Matching

Notification n = new Email("support@example.com", "Welcome", "Hello!");

String result = switch (n) {
    case Email e when e.subject().contains("Welcome") ->
        "Send welcome pack to: " + e.from();
    case Email e ->
        "Archive email from: " + e.from();
    case SMS s when s.message().contains("URGENT") ->
        "High priority SMS to: " + s.number();
    case SMS s ->
        "Standard SMS to: " + s.number();
};

The compiler knows Notification can only be an Email or SMS, so the switch is exhaustive — no need for a default case, and the compiler ensures you cover all branches.


Bonus: Enum Matching Still Shines

While switch on enums isn’t new, you can now use when with them too:

enum Status { ACTIVE, INACTIVE, ERROR }

Status status = Status.ACTIVE;

String message = switch (status) {
    case ACTIVE -> {
		    	if (random) yield "Randomly excited!";
		    	else yield "Just active.";
		    }
    case INACTIVE, ERROR -> "Not working.";
};

Why You Should Care

The new switch:

  • Improves readability: Less boilerplate, fewer casts.
  • Boosts safety: Exhaustiveness checks + strong typing.
  • Works seamlessly with modern Java features like records and sealed types.

It’s one of those quality-of-life upgrades that make writing Java code less verbose and more expressive — especially for domain modeling.


Conclusion

Java 21’s pattern matching for switch is a big step forward. Whether you’re working with polymorphic models, building REST APIs, or writing DSLs, this feature will make your code cleaner and safer.

If you haven’t tried it yet — fire up a Java 21 project and give your old switch statements a makeover.

Here’s a implementation example.