Loslegen
Javaneer
Zurück zur Stufe
Stufe 7·Advanced Concurrency & Modern Java

Modern Java: Records, Sealed Types & Patterns

Model data with records and sealed hierarchies as algebraic data types, and destructure them with pattern matching for switch.

16 Min. LesezeitExperte

Deutsche Übersetzung in Arbeit

Diese Lektion ist noch nicht ins Deutsche übersetzt und wird daher auf Englisch angezeigt. Der Rest der Seite ist vollständig lokalisiert.

Auf dieser Seite

Modern Java (17 through 25) added features that change how you model data and control flow. Records, sealed types, and pattern matching together let you express algebraic data types - a precise, compiler-checked way to model "a value is one of these shapes" - that used to require verbose class hierarchies or unsafe casts.

Records: transparent immutable data

A record is a concise, immutable data carrier. One line generates the constructor, accessors, equals, hashCode, and toString:

record Point(int x, int y) {}                    // done: immutable, with equals/hashCode/toString
record Money(BigDecimal amount, String currency) {}

Records are the right default for DTOs, map keys, and any "just data" type - and, as you'll see, the components of algebraic data types.

Sealed types: a closed set of subtypes

A sealed interface or class restricts which types may implement or extend it. This turns an open hierarchy into a closed one the compiler understands completely:

// a Shape is exactly one of these three - nothing else can implement it
sealed interface Shape permits Circle, Rectangle, Triangle {}
record Circle(double radius) implements Shape {}
record Rectangle(double w, double h) implements Shape {}
record Triangle(double base, double height) implements Shape {}

Because the set is closed, the compiler knows every possible case - which pattern matching exploits.

Pattern matching: destructure and branch exhaustively

Pattern matching for switch tests a value's type and binds its data in one step, and record patterns destructure a record's components directly. Combined with sealed types, the switch is exhaustive - the compiler errors if you miss a case:

// exhaustive, destructuring switch over a sealed type
double area(Shape shape) {
    return switch (shape) {
        case Circle(double r)          -> Math.PI * r * r;
        case Rectangle(double w, double h) -> w * h;
        case Triangle(double b, double h)  -> 0.5 * b * h;
        // no default needed: the compiler knows these are ALL the shapes
    };
}

No casts, no instanceof chains, no forgotten case slipping through - and if you later add a Pentagon, every non-exhaustive switch fails to compile until you handle it. This is the algebraic-data- type style (a "sum of products") that functional languages have long had, now first-class in Java.

Sealed + records + switch = algebraic data types

The three features are designed to work together: sealed types define the closed set of cases (the 'sum'), records define each case's data (the 'product'), and pattern-matching switch consumes them exhaustively. Reach for this to model domains with a fixed set of variants - expression trees, protocol messages, UI states, result-or-error - replacing visitor patterns and instanceof ladders with clear, checked code.

A labeled set of boxes vs. a mystery crate

Before sealed types, a Shape reference was a mystery crate: it might contain anything that claimed to be a Shape, so you'd cautiously feel around with instanceof and casts, and a new kind of shape could show up unannounced and slip past your checks. Sealed types nail the lid to a known set of labeled boxes - Circle, Rectangle, Triangle, and no others. Now the switch can open exactly those boxes, unpack their contents (record patterns), and the compiler guarantees you've accounted for every box - if someone adds a new labeled box later, the build stops until you handle it too.

Model a result type

You want a type representing 'either a success carrying a value, or a failure carrying an error message,' and code that handles both exhaustively. Sketch how sealed types, records, and pattern matching express this, and what the compiler guarantees.

How do sealed types, records, and pattern matching work together?

Key takeaways

  • Records are concise immutable data carriers - constructor, accessors, equals/hashCode/toString generated - ideal for DTOs, keys, and ADT components.
  • Sealed types restrict which classes may implement/extend them, turning an open hierarchy into a closed, compiler-known set.
  • Pattern matching for switch tests type and binds data in one step; record patterns destructure components.
  • Sealed + records + switch express algebraic data types, and the switch is exhaustive - missing a case is a compile error.
  • Use this to model fixed-variant domains (expressions, messages, result-or-error), replacing instanceof ladders and visitor patterns.
War diese Lektion hilfreich?
Diese Seite auf GitHub bearbeiten