Chaturmind
LearnDSASystem DesignBlogPremium
Sign inGet started
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML

Company

  • Blog
  • Premium
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Chaturmind. All rights reserved.

Built for engineers who want to go deep.


← Java 21 — New Features

Data-Oriented Programming

  • Records
  • Sealed Classes
  • Pattern Matching

Virtual Threads (Project Loom)

  • Virtual Threads
Chaturmind
← Java 21 — New Features

Data-Oriented Programming

  • Records
  • Sealed Classes
  • Pattern Matching

Virtual Threads (Project Loom)

  • Virtual Threads
HomeLearnJavaJava 21 New FeaturesJava 21 Features
✓ FreeIntermediate· 12 min read

Pattern Matching

Use instanceof pattern matching, switch pattern matching with guards, and deconstruction patterns.

Published February 26, 2025


Pattern Matching in Java

Pattern matching eliminates the cast-after-instanceof boilerplate and enables expressive, type-safe switch expressions. Introduced incrementally: instanceof (Java 16), switch (Java 21).

instanceof Pattern Matching (Java 16)

// Old way
if (obj instanceof String) {
    String s = (String) obj; // redundant cast
    System.out.println(s.length());
}

// New way: binding variable declared inline
if (obj instanceof String s) {
    System.out.println(s.length()); // s is String, already cast
}

// Works in complex conditions
if (obj instanceof String s && s.length() > 5) {
    System.out.println("Long string: " + s);
}

// Negation
if (!(obj instanceof String s)) {
    throw new IllegalArgumentException("Expected String");
}
// s NOT in scope here

Switch Pattern Matching (Java 21)

public String describe(Object obj) {
    return switch (obj) {
        case Integer i  -> "Integer: " + i;
        case Long l     -> "Long: " + l;
        case Double d   -> "Double: " + d;
        case String s   -> "String of length " + s.length();
        case int[] arr  -> "int array of length " + arr.length;
        case null       -> "null";
        default         -> "Something else: " + obj.getClass().getSimpleName();
    };
}

Guarded Patterns — when guards

public String classify(Object obj) {
    return switch (obj) {
        case Integer i when i < 0  -> "negative int";
        case Integer i when i == 0 -> "zero";
        case Integer i             -> "positive int: " + i;
        case String s when s.isBlank() -> "blank string";
        case String s -> "string: " + s;
        default -> "other";
    };
}

Record Patterns (Java 21) — deconstruction

record Point(int x, int y) {}
record Line(Point start, Point end) {}

Object shape = new Line(new Point(0, 0), new Point(3, 4));

// Deconstruct nested records
if (shape instanceof Line(Point(int x1, int y1), Point(int x2, int y2))) {
    double length = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
    System.out.println("Length: " + length);
}

// In switch
String result = switch (shape) {
    case Line(Point(int x1, int y1), Point(int x2, int y2)) ->
        "Line from (%d,%d) to (%d,%d)".formatted(x1, y1, x2, y2);
    case Point(int x, int y) -> "Point at (%d,%d)".formatted(x, y);
    default -> "Unknown shape";
};

Combining with Sealed Classes

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 int eval(Expr expr) {
    return switch (expr) {
        case Num(int v)           -> v;
        case Add(Expr l, Expr r)  -> eval(l) + eval(r);
        case Mul(Expr l, Expr r)  -> eval(l) * eval(r);
        // Exhaustive — no default needed because Expr is sealed
    };
}

// eval(new Add(new Num(3), new Mul(new Num(4), new Num(5)))) → 23

Practical Use: API Response Handling

sealed interface ApiResult<T> permits ApiResult.Ok, ApiResult.Error {}
record Ok<T>(T data) implements ApiResult<T> {}
record Error<T>(int code, String message) implements ApiResult<T> {}

public ResponseEntity<?> handle(ApiResult<User> result) {
    return switch (result) {
        case Ok(User user) -> ResponseEntity.ok(user);
        case Error(int code, String msg) when code == 404 -> ResponseEntity.notFound().build();
        case Error(int code, String msg) -> ResponseEntity.status(code).body(msg);
    };
}

Interview Tips

  1. Pattern matching + sealed classes is the modern Java way to eliminate instanceof chains and match discriminated unions.
  2. when guards (previously && after the type pattern) make switch expressions far more expressive than traditional switch.
  3. Record deconstruction is powerful for processing nested data without manual field access.

Previous

Sealed Classes

Next

Virtual Threads

AI Tutor

Lesson: Pattern Matching

Quick actions

AI responses can be inaccurate. Verify critical information.