Pattern Matching
1. What Is Pattern Matching?
-
Pattern matching lets Java check whether a value matches a particular pattern and, if it does, extract/use information from it.
-
It makes type checking, casting, and conditional logic shorter and easier to read.
-
Important Java pattern-matching features include:
-
instanceofpatterns -
switchpatterns -
Record patterns
-
-
These features were introduced and finalized across different Java versions.
2. Pattern Matching with Regular Expressions
Regular expressions are another form of pattern matching, mainly used for finding patterns in text.
Example:
Pattern pattern = Pattern.compile("\\bflame\\b");
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
System.out.println(matcher.group());
}
Here:
Pattern
↓
"flame"
↓
Search inside text
↓
Matcher finds matching occurrences
Useful classes:
Pattern
Matcher
3. Pattern Matching with instanceof
Since Java 16, instanceof can directly test a type and create a variable for the matched object.
Traditional approach
Before pattern matching:
if (o instanceof String) {
String s = (String) o;
System.out.println(s.length());
}
With pattern matching:
if (o instanceof String s) {
System.out.println(s.length());
}
This:
o instanceof String s
means:
Is o a String?
↓
Yes
↓
Create variable s containing that String
s is called a pattern variable.
4. Pattern Variables and Scope
The pattern variable only exists where Java knows that the pattern matched.
Example:
if (o instanceof String s) {
System.out.println(s.length());
}
You can also combine it with &&:
if (o instanceof String s && !s.isEmpty()) {
System.out.println(s);
}
Here s is available in the second condition because && evaluates it only after the first condition succeeds.
You can also use it after a condition that exits:
if (!(o instanceof String s)) {
return;
}
System.out.println(s.length());
After the return, Java knows that o must be a String, so s is available.
5. Pattern Matching Makes equals() Cleaner
Traditional code often requires:
if (!(o instanceof Point)) {
return false;
}
Point point = (Point) o;
return x == point.x && y == point.y;
Pattern matching simplifies this:
public boolean equals(Object o) {
return o instanceof Point point
&& x == point.x
&& y == point.y;
}
So pattern matching combines:
type checking
+
casting
+
variable declaration
into one expression.
6. Pattern Matching with switch
Since Java 21, switch can match objects using type patterns.
Example:
String result = switch (o) {
case Integer i -> "Integer: " + i;
case Long l -> "Long: " + l;
case Double d -> "Double: " + d;
default -> "Other: " + o;
};
Instead of:
if (o instanceof Integer) {
...
} else if (o instanceof Long) {
...
} else if (o instanceof Double) {
...
}
the switch handles all the cases together.
The important idea is:
o
↓
Integer? → i
Long? → l
Double? → d
Other? → default
The pattern variable is available inside its corresponding case:
case Integer i -> System.out.println(i);
7. Guarded Cases with when
A switch pattern can also have an additional condition using when.
Example:
String result = switch (o) {
case String s when !s.isEmpty() -> "Non-empty: " + s;
case String s -> "Empty string";
default -> "Not a string";
};
The first case means:
Is it a String?
+
Is it NOT empty?
So when adds an additional condition to the pattern.
8. Record Patterns
A record pattern allows you to match a record and extract its components directly.
Suppose:
record Point(int x, int y) {}
Without a record pattern:
if (o instanceof Point p) {
int x = p.x();
int y = p.y();
}
With a record pattern:
if (o instanceof Point(int x, int y)) {
System.out.println(x);
System.out.println(y);
}
The record is automatically deconstructed:
Point
↓
(int x, int y)
↓
x and y are directly available
This is especially useful when working with records containing multiple components.
9. Pattern Matching in switch + Records
Record patterns can also be used inside switch.
String describe(Object obj) {
return switch (obj) {
case Point(int x, int y) ->
"Point at " + x + ", " + y;
default ->
"Unknown object";
};
}
This combines:
switch pattern
+
record pattern
+
automatic extraction
10. Pattern Matching vs Traditional Code
Traditional
if (obj instanceof Point) {
Point p = (Point) obj;
if (p.x() > 0) {
System.out.println(p.x());
}
}
Pattern matching
if (obj instanceof Point(int x, int y) && x > 0) {
System.out.println(x);
}
The newer version avoids unnecessary casting and temporary variables.
Quick Revision
| Feature | Java | Example | Purpose |
|---|---|---|---|
| Regular expressions | — | Pattern.compile(...) |
Match text patterns |
instanceof pattern |
16+ | o instanceof String s |
Type check + variable |
switch pattern |
21+ | case String s -> |
Match different types |
when guard |
21+ | case String s when ... |
Add a condition |
| Record pattern | 21+ | Point(int x, int y) |
Extract record components |
The main idea
Traditional Java:
check type
↓
cast
↓
create variable
↓
use variable
Pattern matching:
check type + create variable
↓
use variable
For example:
if (obj instanceof String s) {
System.out.println(s.length());
}
Read this as:
"If
objis aString, give me that String ass."
And:
if (obj instanceof Point(int x, int y)) {
System.out.println(x + y);
}
Read this as:
"If
objis aPoint, give me itsxandycomponents."