Java 21 - Pattern matching

 

What Is Pattern Matching?

 

Pattern matching is a powerful language feature that simplifies code by allowing developers to express complex conditional logic more concisely.
It enhances readability and reduces boilerplate code.

 

Java 11: The Pre-Pattern Matching Era

 

In Java 11, we didn’t have native pattern matching.
We often resorted to verbose if-else constructs or switch statements to handle different cases.
These approaches could lead to code duplication and were error-prone.

 

Java 21: The Arrival of Pattern Matching

(part of Project Amber)

 

With Java 21, pattern matching has become an integral part of the language.
Let’s explore its key aspects:

 

  • Record Patterns:
     

    See previous article about records in java

    • Record patterns allow us to match against specific record types.

    • Records are a concise way to define classes with immutable data.

    • Example

      record Person(String name, int age) { }
      
      // Pattern matching in switch
      public String getPersonInfo(Person person) {
          return switch (person) {
              case Person p when p.age() < 18 -> "Minor";
              case Person p when p.age() >= 18 -> "Adult";
              default -> "Unknown";
          };
      }
      
  • Pattern Matching for Switch Statements:

    • We can now use patterns directly in switch statements.

    • Simplifies handling different cases based on patterns.

    • Example

      public String processShape(Shape shape) {
          return switch (shape) {
              case Circle c -> "Circle with radius " + c.radius();
              case Rectangle r -> "Rectangle with dimensions " + r.width() + "x" + r.height();
              default -> "Unknown shape";
          };
      }
      
  • Pattern Matching and Type Checking
    Pattern matching simplifies type checking and casting by combining them into a single expression.
    It allows us to match patterns against an object’s type and extract relevant information.

 

    • Type Patterns
      In Java, type patterns are a form of pattern matching.
      They involve checking whether an object is an instance of a specific type and optionally binding it to a variable.

      if (o instanceof String s) {
          System.out.printf("Object is a string: %s%n", s);
      } else if (o instanceof Number n) {
          System.out.printf("Object is a number: %s%n", n);
      }
      
    • Type Casting with Pattern Matching
      When using type patterns, casting happens implicitly.
      If the condition matches, the variable (s or n in the example) is automatically cast to the specified type.
      No explicit casting (e.g., (String) o) is needed.

    • Benefits of Casting via Pattern Matching
      The code becomes more concise and self-explanatory.
      Implicit casting reduces the risk of runtime errors due to incorrect casts.
      No need for explicit type checks followed by casting.

      Suppose we want to convert different types (e.g., Integer, Float) to double.
      Using pattern matching, we can achieve this elegantly:

      static double getDoubleUsingPatternMatching(Object o) {
          return switch (o) {
              case Integer i -> i.doubleValue();
              case Float f -> f.doubleValue();
              default -> 0.0;
          };
      }
      

 

Why Use Pattern matching (and why not to use it)?

 

Pattern matching simplifies complex conditional logic, making code more readable. 
Instead of lengthy if-else chains, you can express conditions more succinctly using patterns.

Fewer lines of code mean less boilerplate.
Pattern matching allows you to express intentions directly.

scenarios where using pattern matching might not be the best choice:

 

  • Overcomplicating Simple Conditions:

    • Disadvantage: For straightforward checks (e.g., equality comparisons), pattern matching can add unnecessary complexity.

    • Recommendation: Stick to traditional if-else or switch statements for simple cases.

  • Boolean Expressions:

    • Disadvantage: Pattern matching is not designed for evaluating boolean expressions directly.

    • Recommendation: Use regular logical operators (e.g., &&||) for boolean conditions.

  • Performance-Critical Code:

    • Disadvantage: In performance-critical sections, pattern matching might introduce overhead.

    • Recommendation: Profile your code to ensure pattern matching doesn’t impact performance significantly.

  • Complex Nested Patterns:

    • Disadvantage: Overly complex nested patterns can reduce readability.

    • Recommendation: Break down convoluted patterns into simpler conditions.

 

Remember that pattern matching is a tool, and like any tool, it should be used judiciously based on the specific problem you’re solving.
Choose wisely! 🛠️

 

Have fun with patterns in Java 21 😊