Demystifying Pattern Matching in Java Demystifying Pattern Matching in Java

Demystifying Pattern Matching in Java

In this blog post, we’ll explore what pattern matching is and how it’s used.


What is Pattern Matching?

Pattern Matching is a mechanism that allows you to test the structure of an object and, at the same time, extract data from it — in a safe and expressive way.

In simpler terms, think of pattern matching as a smarter instanceof that lets you match and bind an object to a variable at the same time, without awkward casting.


Pattern Matching for instanceof (Java 16+)

Before pattern matching:

if (obj instanceof String) {
String s = (String) obj;
System.out.println(s.toLowerCase());
}

After pattern matching:

if (obj instanceof String s) {
System.out.println(s.toLowerCase());
}

In this case you can see that if the obj is an instance of String, Java will automatically cast to String and assign it to variable s.

Cleaner, safer, and more expressive.


Pattern Matching for switch (Java 21+)

Java 21 brings pattern matching to switch statements — combining destructuring, type checks, and control flow in one elegant block.

Classic Switch with Type Checks (Clunky)

static String format(Object obj) {
if (obj instanceof Integer i) {
return "int: " + i;
} else if (obj instanceof String s) {
return "string: " + s.toUpperCase();
} else {
return "unknown";
}
}

Pattern Matching with switch

static String format(Object obj) {
return switch (obj) {
case Integer i -> "int: " + i;
case String s -> "string: " + s.toUpperCase();
default -> "unknown";
};
}

Note that in this case, if the argument obj passed to the method is not of type Integer, no exception will be thrown. This is amazing because you don’t need to worry about Class Cast Exceptions.

So for the example. the code below:

System.out.println(format(10));
System.out.println(format("afrancodev"));
System.out.println(format(10.0f));

will output:

Terminal window
int: 10
string: AFRANCODEV
unknown

Pattern Matching with Record Classes (Java 21+)

One of the killer features is destructuring record components during pattern matching.

Pattern matching in switch

Given a record:

record Point(int x, int y) {}

You can desconstruct the object in a switch expression:

static String describe(Object obj) {
return switch (obj) {
case Point(int x, int y) -> "Point at (" + x + ", " + y + ")";
default -> "Unknown object";
};
}

For example, the code below:

System.out.println(describe(new Point(10, 10)));
System.out.println(describe(new Point(10, 20)));

will output:

Terminal window
Point at (10, 10)
Point at (10, 20)

With just one line, you’ve matched the type and extracted its fields. Incredible!


Guarded Patterns

What if you want to match with a condition? That’s where guarded patterns shine:

static String describe(Point p) {
return switch (p) {
case Point(int x, int y) when x == y -> "Diagonal point";
case Point(int x, int y) -> "Regular point at (" + x + "," + y + ")";
};
}

Here, we use a guard when (x == y) to refine the match.

For example, the code below:

System.out.println(describe(new Point(10, 10)));
System.out.println(describe(new Point(10, 20)));

will output:

Terminal window
Diagonal point
Regular point at (10,20)

Why Pattern Matching Matters

Java has always prioritized readability and type safety, but its verbosity has often been a pain point. Pattern matching addresses this by:

  • Reducing boilerplate
  • Improving control flow readability
  • Eliminating unsafe casting
  • Enabling future functional-style constructs

Combine this with records, sealed types, and new switch expressions, and you’ve got a Java that’s modern, expressive, and still safe.


Pattern matching in Java is one of the most exciting steps toward declarative, clean, and powerful programming. As Java evolves, expect even more robust features like array matching, deep destructuring, and pattern unions.

So if you haven’t explored Java 21 yet — now is the time to match with the future.

Happy Coding!


← Back to blog