Record Patterns

Quick summary:

  • Lets us use pattern matching on records.
  • Released in Java 21 (2023), but introduced in Java 19 (2022).

Key Features

  • Deconstruction of Records: Enables pattern matching to automatically deconstruct records based on their components.
  • Simplified Syntax: Reduces the verbosity of extracting and checking record components, streamlining how developers handle complex data structures.
  • Enhanced Readability: Improves code clarity by allowing direct access to record components in instanceof expressions, making conditions easier to read and understand.
  • Integration with instanceof: Seamlessly integrates with existing instanceof checks, allowing for conditional actions based on record content in a type-safe manner.

Example

Before Java 21:

Before, you had manually cast an object after asserting it was possible with an instanceof operation.

        for (Shape shape : shapes) {
            if(shape instanceof Circle) {
                Circle c = (Circle) shape;
                System.out.printf("Circle with radius %.1f", c.radius());
            } else if(shape instanceof Rectangle) {
                Rectangle r = (Rectangle) shape;
                System.out.printf("Rectangle with width %.1f and height %.1f", r.width(), r.height());
            }
            System.out.printf(" and area %.1f and perimeter %.1f\n", shape.calcArea(), shape.calcArea());
        }

After Java 21:

With the new pattern matching, the use of instanceof immediately automatically casts the object to that type and immediately makes its components available for local use.

        for (Shape shape : shapes) {
            if(shape instanceof Circle(double radius)) {
                System.out.printf("Circle with radius %.1f", radius);
            } else if(shape instanceof Rectangle(double width, double height)) {
                System.out.printf("Rectangle with width %.1f and height %.1f", width, height);
            }
            System.out.printf(" and area %.1f and perimeter %.1f\n", shape.calcArea(), shape.calcArea());
        }

Notice how we don’t have to call the properties via the object, we have immediate access to those values.


👩‍💻 Hands-on Demo: Pattern Matching for instanceof

  1. Write a Pet-interface with an implementing Cat-class according to the UML provided..
  2. In the main-method of a PetsApp-class, make an array of pets.
  3. Write a processPet-method that accepts a Pet-object and, if it’s a cat, prints its name and age.
  4. Back in the main-method, have each of your Cat-objects in your array processed using the processPet-method.
  5. We only want to process cats that are younger than 2 years old: adapt your pattern matching such that you also check for age, then print something accordingly.
  1. Add another printPetJson-method which accepts a pet, and prints it in JSON-format.
    • Use pattern matching with a null-check for the name component to solve this.
  2. Have all your pets printed in JSON-format.

Solutions

🕵️‍♂️ Click here to reveal the solutions
public class PetApp {
    public static void main(String[] args) {
        Pet[] pets = {
                new Cat("Fluffy", 2),
                new Cat("Picasso", 7),
                new Cat("Dorado", 1)
        };

        Arrays.stream(pets)
                .peek(PetApp::processPet)
                .forEach(PetApp::printPetJson);

    }

    private static void processPet(Pet pet) {
        if (pet instanceof Cat(String name, int age) && age < 2) {
            System.out.println(name + " is still just a kitty!");
        }
    }

    private static void printPetJson(Pet pet) {
        if (pet instanceof Cat(String name, int age) && name != null) {
            System.out.printf("""
                    {
                        "name": "%s",
                        "age": %d
                    }
                    """, name, age);
        } else {
            System.out.println("Unsupported pet type");
        }
    }
}
public interface Pet {
}
public record Cat(String name, int age) implements Pet {
}

Summary

  • Pattern Matching on Records: Introduced in Java 21, Record Patterns allow for pattern matching directly on records, which automatically deconstructs them into their components, enhancing code simplicity and readability.
  • Simplified Syntax and Readability: Reduces complexity by allowing direct access to record components without explicit casting, streamlining the handling of complex data structures and making conditions easier to understand.
  • Integration with instanceof: Seamlessly works with instanceof checks, enabling conditional actions and immediate access to component values in a type-safe way, thus avoiding additional property calls.