Java Pattern Matching, Sealed Classes and Records

Modern Java (17, 21 and 25) has features which make code shorter and safer. The three most important work together: sealed types, records and pattern matching.

Sealed classes and interfaces

A sealed type says exactly which classes are allowed to extend or implement it. So the compiler knows all the possible cases.

sealed interface Shape permits Circle, Rectangle, Triangle { }

record Circle(double radius) implements Shape { }
record Rectangle(double width, double height) implements Shape { }
record Triangle(double base, double height) implements Shape { }

public class Main {
    static double area(Shape shape) {
        return switch (shape) {
            case Circle c -> Math.PI * c.radius() * c.radius();
            case Rectangle r -> r.width() * r.height();
            case Triangle t -> 0.5 * t.base() * t.height();
        };                                             // no default needed
    }

    public static void main(String[] args) {
        System.out.printf("%.2f%n", area(new Circle(2)));
        System.out.printf("%.2f%n", area(new Rectangle(3, 4)));
        System.out.printf("%.2f%n", area(new Triangle(6, 5)));
    }
}
Output

12.57
12.00
15.00

Because Shape is sealed, the switch covers all the cases and needs no default. If you add a new shape and forget the switch, the compiler gives an error.

Pattern matching for switch

A switch can test the type of an object and use a guard (when):

public class Pattern {
    static String describe(Object o) {
        return switch (o) {
            case null -> "nothing";
            case Integer i when i > 100 -> "big number " + i;
            case Integer i -> "number " + i;
            case String s when s.isEmpty() -> "empty text";
            case String s -> "text of length " + s.length();
            case int[] a -> "int array of " + a.length;
            default -> "something else: " + o;
        };
    }

    public static void main(String[] args) {
        System.out.println(describe(5));
        System.out.println(describe(500));
        System.out.println(describe(""));
        System.out.println(describe("hello"));
        System.out.println(describe(new int[3]));
        System.out.println(describe(null));
        System.out.println(describe(2.5));
    }
}
Output

number 5
big number 500
empty text
text of length 5
int array of 3
nothing
something else: 2.5

Record patterns

A pattern can take a record apart and give you its parts at once:

record Point(int x, int y) { }
record Line(Point from, Point to) { }

public class Rec {
    static String show(Object o) {
        if (o instanceof Point(int x, int y)) {
            return "Point at " + x + "," + y;
        }
        if (o instanceof Line(Point(var x1, var y1), Point(var x2, var y2))) {
            return "Line from " + x1 + "," + y1 + " to " + x2 + "," + y2;
        }
        return "unknown";
    }

    public static void main(String[] args) {
        System.out.println(show(new Point(1, 2)));
        System.out.println(show(new Line(new Point(0, 0), new Point(5, 5))));
        System.out.println(show("text"));
    }
}
Output

Point at 1,2
Line from 0,0 to 5,5
unknown

A small expression calculator

This is a classic use: a sealed interface, records and a recursive switch.

sealed interface Expr permits Num, Add, Mul { }

record Num(int value) implements Expr { }
record Add(Expr left, Expr right) implements Expr { }
record Mul(Expr left, Expr right) implements Expr { }

public class Calc {
    static int eval(Expr e) {
        return switch (e) {
            case Num n -> n.value();
            case Add(Expr l, Expr r) -> eval(l) + eval(r);
            case Mul(Expr l, Expr r) -> eval(l) * eval(r);
        };
    }

    public static void main(String[] args) {
        Expr e = new Add(new Num(2), new Mul(new Num(3), new Num(4)));   // 2 + 3 * 4
        System.out.println(eval(e));
    }
}
Output

14

What each feature gives

FeatureSinceBenefit
RecordsJava 16Short data classes
Pattern matching for instanceofJava 16No manual cast
Sealed classesJava 17A closed list of subtypes
Pattern matching for switchJava 21Type tests with guards, no long if chains
Record patternsJava 21Take a record apart in one step
Note: Together these features give you something like the “algebraic data types” of functional languages, with the safety of the compiler. Next: Java Cheat Sheet.

Congratulations! Chapter Finished. Learn more about the similar topics:
Exercises & Assignments
No Content Found.
Interview Questions & Answers
No Content Found.