Java Pattern Matching for Switch


Java 17 brings a new language feature called Pattern Matching for Switch as a preview. This feature allows case labels with patterns instead of constants and can be used in switch statements and expressions. This blog will explore how to use pattern matching in switch with examples and output.

What is Pattern Matching for Switch?

Traditionally, switch statements in Java have been limited to primitive types and enums. Pattern matching for switches extends this capability by allowing developers to use more complex patterns, including type patterns and deconstruction patterns. This enables concise and expressive code when dealing with complex data structures.

Syntax

The syntax for pattern matching in switch statements looks like this:

result = switch (expression) {
    case pattern1 -> expression1;
    case pattern2 -> expression2;
    // more cases...
    default -> defaultExpression;
};

Here, expression is evaluated, and its value is compared against each case’s pattern. If a match is found, the corresponding expression is executed.

Let’s walk through some examples to understand pattern matching for switch better.

1. Type Pattern Matching

In this, we can match on type patterns using the following syntax:

public static String typedPatternMatching(Object o) {
        return switch (o) {
            case null -> "I am null";
            case String s -> "I am a String. My value is " + s;
            case Integer i -> "I am an int. My value is " + i;
            case Double d -> "I am a double. My value is " + d;
            default -> "I am of an unknown type. My value is " + o.toString();
        };
}

Output:

> typedPatternMatching("HELLO")
"I am a String. My value is HELLO"

> typedPatternMatching(123)
"I am an int. My value is 123"

> typedPatternMatching(null)
"I am null"

> typedPatternMatching(0.5)
"I am a double. My value is 0.5"

2. Guarded Pattern Matching

We can also use guarded patterns to add additional conditions to patterns:

public static String guardedPatternMatching(Object o) {
        return switch (o) {
            case String s && s.length() > 0 -> "I am a non-empty String. My value is " + s;
            case Integer i && i > 10 -> "I am an integer greater than 10. My value is " + i;
            default -> "I am of an unknown type or value";
        };
}

Output:

> guardedPatternMatching("HELLO")
"I am a non-empty String. My value is HELLO"

> guardedPatternMatching(0)
"I am of an unknown type or value"

> guardedPatternMatching(12)
"I am an integer greater than 10. My value is 12"

> guardedPatternMatching(null)
"I am of an unknown type or value"

3. Guarded Patterns with Boolean Expressions

We can use guarded patterns to add Boolean expressions to patterns. For example:

public static double getDoubleValueUsingGuardedPatterns(Object o) {
        return switch (o) {
            case String s && s.matches("[+-]?\\d+(\\.\\d+)?") -> Double.parseDouble(s);
            case Integer i -> (double) i;
            default -> 0d;
        };
}

Output:

> getDoubleValueUsingGuardedPatterns("3.14")
3.14

> getDoubleValueUsingGuardedPatterns(2)
2.0

> getDoubleValueUsingGuardedPatterns(null)
0.0

4. Matching Complex Objects

record Point(int x, int y) {}

Point p = new Point(2, 3);
String position = switch (p) {
    case Point(0, 0) -> "Origin";
    case Point(x, y) when x > 0 && y > 0 -> "Quadrant I";
    case Point(x, y) when x < 0 && y > 0 -> "Quadrant II";
    case Point(x, y) when x < 0 && y < 0 -> "Quadrant III";
    case Point(x, y) when x > 0 && y < 0 -> "Quadrant IV";
    default -> "On the axis";
};

System.out.println("Position: " + position);

Output:

Position: Quadrant I

Related Post: Java 17 Sealed Classes

5. Cover All Possible Values

When using switch expressions, it’s important to ensure that the case labels cover all possible input values. For example, the following code will not compile because the switch expression does not cover all possible input values:

public static Object get(Collection c) {
        return switch (c) {
            case ArrayList a -> a;
        };
}

Output:

[ERROR] SwitchPatternMatching.java:25: error: the switch expression does not cover all possible input values return switch (c).

To fix this error, we need to add a default case or cover all possible input values.

public static Object get(Collection c) {
        return switch (c) {
            case ArrayList a -> a;
            default -> throw new IllegalStateException("Unexpected value: " + c);
        };
 }

Summary

This feature allows case labels with patterns instead of just constants, and can be used in both switch statements and switch expressions.

References

  1. Pattern Matching for switch (Preview)
  2. Sealed Interface in Java 17

Similar Posts

About the Author

Atul Rai
I love sharing my experiments and ideas with everyone by writing articles on the latest technological trends. Read all published posts by Atul Rai.