How to use improved pattern matching for switch in Java 25

Java 25 introduces an improved feature for pattern matching with switch, further streamlining type checks, instance checks, and value comparisons.

Here’s how you can effectively use the enhanced pattern matching for switch in Java 25:

Key Features

  1. Exhaustive Matching: Ensures that all possible branches are accounted for.
  2. Simplification of Null Handling: Handles null conditions without extra boilerplate.
  3. Nested Patterns in Switch: Allows patterns to be nested for cleaner and more expressive logical flows.
  4. Constant Matching: Can combine constants with patterns.
  5. Sealed Class Support: Works seamlessly with sealed classes, auto-detecting subclasses for exhaustive pattern checks.

Syntax Examples

1. Type Pattern Matching

Allows you to handle specific types directly in a switch.

public static String process(Object obj) {
    return switch (obj) {
        case String s -> "It's a String: " + s;
        case Integer i -> "It's an Integer: " + (i + 10);
        case null -> "It's null!";
        default -> "Unknown type!";
    };
}

2. Guarded Patterns

You can add additional conditions to patterns with when clauses.

public static String process(Number num) {
    return switch (num) {
        case Integer i when i > 0 -> "Positive Integer: " + i;
        case Integer i -> "Other Integer: " + i;
        case Double d -> "Double: " + d;
        default -> "Unknown Number type!";
    };
}

3. Exhaustive Matching with sealed Classes

For sealed class hierarchies, switch ensures all subclasses are accounted for.

public sealed interface Shape permits Circle, Rectangle {}

public record Circle(double radius) implements Shape {}
public record Rectangle(double length, double width) implements Shape {}

public static String shapeInfo(Shape shape) {
    return switch (shape) {
        case Circle c -> "Circle with radius: " + c.radius();
        case Rectangle r -> "Rectangle with dimensions: " + r.length() + " x " + r.width();
    };
}

In this case, if you miss a subclass (like Rectangle), the compiler will throw an exhaustiveness error.

4. Null Handling Simplification

Switch patterns now handle null explicitly or exclude it in non-nullable cases.

public static void handleInput(String input) {
    switch (input) {
        case null -> System.out.println("Input is null!");
        case "SpecificValue" -> System.out.println("Matched SpecificValue");
        default -> System.out.println("Fallback case");
    }
}

Benefits of Improved Pattern Matching

  • Cleaner Code: Avoid type casts and complex if-else chains.
  • More Readable: Logic becomes more declarative and expressive.
  • Compile-Time Safety: Exhaustive checking ensures safer code.
  • Null-Safety: Simplifies handling of null values in branching.

These improvements make switch not just a control-flow statement but a powerful tool for type- and value-based pattern matching.

How to Use Pattern Matching with instanceof in Java 17

Pattern matching with the instanceof operator was introduced in Java 16 (as a preview feature) and became a standard feature in Java 17. It simplifies the process of type casting when checking an object’s type, making the code shorter and more readable.

Here’s how you can use pattern matching with instanceof in Java 17:

Syntax

With pattern matching, you can directly declare a local variable while checking the type with instanceof. If the condition is true, the variable is automatically cast to the specified type, and you can use it without explicit casting.

if (object instanceof Type variableName) {
   // Use variableName, which is already cast to Type
}

Key Features:

  1. Type Checking and Casting in One Step: No need for an explicit cast.
  2. Shorter Code: Reduces boilerplate.
  3. Available Within Scope: The variable is accessible only within the scope of the if block where the condition is evaluated as true.
  4. Guarded Pattern (Available in Java 20+ – Preview): Introduced later, allowing additional conditions within instanceof.

Example 1: Basic Usage

package org.kodejava.basic;

public class PatternMatchingExample {
   public static void main(String[] args) {
      Object obj = "Hello, Java 17!";

      if (obj instanceof String str) {
         // Type already checked and cast to `String`
         System.out.println("String length: " + str.length());
      } else {
         System.out.println("Not a string.");
      }
   }
}

Explanation:

  • The variable str is declared and automatically cast to String in the same instanceof statement.
  • Within the if block, you can directly use str as it is guaranteed to be a String.

Example 2: Pattern Matching in Loops

package org.kodejava.basic;

import java.util.List;

public class PatternMatchingExample {
   public static void main(String[] args) {
      List<Object> objects = List.of("Java", 42, 3.14, "Pattern Matching");

      for (Object obj : objects) {
         if (obj instanceof String str) {
            System.out.println("Found a String: " + str.toUpperCase());
         } else if (obj instanceof Integer num) {
            System.out.println("Found an Integer: " + (num * 2));
         } else if (obj instanceof Double decimal) {
            System.out.println("Found a Double: " + (decimal + 1));
         } else {
            System.out.println("Unknown type: " + obj);
         }
      }
   }
}

Output:

Found a String: JAVA
Found an Integer: 84
Found a Double: 4.14
Found a String: PATTERN MATCHING

Example 3: Combining && Conditions

You can combine pattern matching with additional conditions:

package org.kodejava.basic;

public class PatternMatchingExample {
   public static void main(String[] args) {
      Object obj = "Hello";

      if (obj instanceof String str && str.length() > 5) {
         System.out.println("String is longer than 5 characters: " + str);
      } else {
         System.out.println("Not a long string (or not a string at all).");
      }
   }
}

Notes:

  1. Scope of Variable:
    The variable introduced inside the instanceof is only accessible inside the block where the condition is true. For example:

    if (obj instanceof String str) {
       System.out.println(str); // str is available here
    }
    // System.out.println(str); // ERROR: str not available here
    
  2. Null Safety:
    If the object being matched is null, the instanceof check will return false, so you don’t have to handle nulls manually.

Benefits:

  • Simplifies code structure.
  • Eliminates the need for verbose casting.
  • Improves readability and reduces errors associated with unnecessary manual typecasting.

Pattern matching with instanceof is now widely used in modern Java. Make sure you’re using JDK 17 or later to take advantage of this feature!