Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Project Amber is making Java more concise and more explicit about data and type hierarchies. Records describe data with less boilerplate, sealed types define a closed set of permitted implementations, and pattern matching lets code inspect and decompose those types directly. Together, they let the compiler check whether a switch handles every case in a closed model.

What Project Amber is

Project Amber is an OpenJDK language-design project focused on improving Java’s everyday language ergonomics. Rather than replacing Java’s object-oriented model, its features make common tasks—describing data, testing types, and branching on known alternatives—less ceremonious and easier for the compiler to analyze.

Its most important ideas reinforce one another. A record makes its data components explicit. A sealed class or interface limits which types can extend or implement it. Pattern matching can then test a value and, when appropriate, extract its components. OpenJDK’s Amber design note describes this synergy: records are easy to decompose, while sealed types give the compiler information it can use to check whether a switch covers all permitted subtypes.

Which Amber features matter most

Feature What it changes Version and status established by the cited documentation
Records Declare a data carrier by listing its components, rather than writing routine fields, accessors, and state-based methods by hand. Documented as part of modern Java in Oracle’s Java SE 21 language documentation. The supplied documentation does not specify the first release for this feature.
Sealed classes and interfaces Restrict a hierarchy to explicitly permitted direct subtypes, making a closed model visible in the type declarations. Documented as part of modern Java in Oracle’s Java SE 21 language documentation. The supplied documentation does not specify the first release for this feature.
Switch expressions Use a switch to produce a value, making branching useful in expression-oriented code. Included in the release-by-release language history cited by Oracle’s Java SE 24 and 25 pages; the supplied material does not specify a first release.
Pattern matching for instanceof Test a value’s type and bind a variable of that type in the same expression, avoiding a separate cast in the usual case. Included in Oracle’s Java SE 21 language documentation and the release history cited for Java SE 24 and 25; the supplied material does not specify a first release.
Pattern matching for switch Branch on types and bind matched values in a switch; with a suitable sealed hierarchy, the compiler can check coverage of permitted alternatives. Permanent in Java SE 21, according to Oracle’s Java SE 21 language documentation.
Record patterns Match a record and bind its components directly, including in nested data structures. Included in the release history cited by Oracle’s Java SE 24 and 25 pages; the supplied material does not specify a first release.
Text blocks Write multi-line string literals more readably, useful for embedded text such as SQL or formatted content. Documented as part of modern Java in Oracle’s Java SE 21 language documentation. The supplied documentation does not specify the first release for this feature.
Primitive pattern work and module import declarations Oracle’s Java 23 announcement describes extending pattern matching to primitive types in instanceof and switch, and introducing module import declarations to simplify reuse of modular libraries. Described as language work in Oracle’s Java 23 announcement. That announcement alone does not establish the final status or availability in a later target JDK.

The release distinctions matter: Java evolves through successive JDKs, and a feature discussed in a release announcement is not automatically a permanent feature of that release. Java SE 21 documents pattern matching for switch as permanent. For any other feature—especially primitive-pattern or module-import work—check the language documentation for the exact JDK you plan to build and run with, and confirm whether the feature is final or preview.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How records, sealed types, and patterns work together

Describe data once with a record

A declaration such as record Point(int x, int y) {} says that a point’s state consists of two components. Java supplies the standard component accessors, a constructor, and equality and hash behavior based on that state. That is a compact alternative to writing routine data-carrier code manually.

The compactness comes with a design commitment: a record’s component list is part of its API and representation. Use a record when that public data shape is an intentional part of the type’s contract, not merely because the syntax is shorter. If the representation should remain hidden or evolve independently of the API, an ordinary class may be a better fit.

Make a hierarchy closed with a sealed interface

A sealed type names the direct implementations or subclasses it permits. In a model such as a shape hierarchy, that declaration communicates that the alternatives are known rather than open-ended. This is useful when the domain really is closed—for example, the variants of an expression node, protocol message, or domain event.

Let a pattern switch inspect the alternatives

Here is a Java 21-style example using records, a sealed interface, and pattern matching for switch:

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
sealed interface Shape permits Circle, Rectangle {}
record Circle(double radius) implements Shape {}
record Rectangle(double width, double height) implements Shape {}

static double area(Shape shape) {
    return switch (shape) {
        case Circle c -> Math.PI * c.radius() * c.radius();
        case Rectangle r -> r.width() * r.height();
    };
}

Each arm names a permitted shape and binds it to a variable of the matching type. Because the switch covers the alternatives in this closed hierarchy, the compiler can check that a case has not been left out; a catch-all default is not needed in this example. If the hierarchy or switch changes, that exhaustiveness check can surface a missing branch at compile time.

Deconstruct records when their components are what you need

A record pattern can bind components directly instead of first binding the record and then calling accessors:

static double areaWithoutNegativeRadius(Shape shape) {
    return switch (shape) {
        case Circle(double radius) -> radius < 0
                ? 0
                : Math.PI * radius * radius;
        case Rectangle(double width, double height) -> width * height;
    };
}

This is especially useful for nested data, where a pattern can express the shape being matched close to the logic that handles it. Use it when component-level matching improves clarity; a type pattern followed by accessor calls may be easier to read when the components are not central to the branch.

What changes compared with older Java

Concern Older, more manual approach Amber-era approach
Data-carrier boilerplate Write and maintain routine fields, constructors, accessors, and state-based methods. Declare a record’s component list and receive standard data-carrier behavior.
Type tests and casts Test a value’s type, cast it separately, and keep the test and cast aligned. Use a type pattern to test and bind the appropriately typed value together.
Closed hierarchy Hierarchy openness may not be apparent from the declarations, making it harder for code and readers to know the intended alternatives. Use a sealed declaration to list permitted direct subtypes.
Missing branches Manual type checks do not inherently give the compiler a complete set of alternatives to audit. With a sealed hierarchy and an exhaustive pattern switch, the compiler can identify an omitted permitted case.
Representation and API An ordinary class can hide its fields behind an API, allowing internal representation to differ from the public contract. A record makes its components part of its public data description; that explicit coupling is the source of much of its concision.
Release readiness Older code may target a wider range of JDKs. Each language feature has its own minimum JDK and final-or-preview history; confirm both for the project’s target JDK before adopting it.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

When these features are a good fit

  • Use records for value-like data whose components should be visible as part of the API, such as coordinates, request values, or immutable domain data.
  • Use sealed types when the set of direct variants is intentionally controlled and the model benefits from being closed.
  • Use pattern switches when behavior naturally depends on the type or variant of a value and a compiler-checked set of cases is valuable.
  • Use record patterns when matching component values directly makes branching clearer, particularly for nested data.
  • Use text blocks when multi-line string content is easier to read in its natural layout than as a chain of escaped string fragments.

These features do not make every class a record, every interface sealed, or every conditional a pattern switch. They are most valuable when the code’s real model is data-oriented and its alternatives are deliberately finite. An extensible plugin API, for example, may need open implementations; making it sealed solely to gain exhaustive switches would misrepresent that design.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What teams should check before adopting Amber features

Confirm the minimum JDK and preview status

Choose a target JDK that supports the feature in the status you intend to use. A permanent language feature is different from one that requires preview enablement, and preview features can change between releases. Check the target JDK’s official language documentation and build configuration rather than inferring availability from a release announcement or from another feature’s history.

Assess source and binary compatibility

New syntax requires compilers that understand it, so teams supporting older JDKs may need to keep older source forms or raise their minimum toolchain and runtime versions. Review the project’s source and target settings, CI images, deployment JREs, and downstream consumers before changing public APIs. A concise declaration is not a compatibility plan.

Review serialization and API contracts

Records make their state description explicit, so converting a class to a record can change what callers see as the type’s canonical data shape. Review serialization behavior, schema expectations, constructors, reflection-based frameworks, and compatibility promises before migrating existing types. Do not assume that a record is a drop-in replacement merely because it represents the same fields today.

Compare records with Lombok deliberately

Records and Lombok data-class-style annotations both reduce boilerplate, but they are not interchangeable. A Java record is a language-level type with a fixed component-based data model. Lombok generates code for ordinary classes through its annotation-processing workflow and can support shapes and customization that a record does not. Choose based on API design, supported JDKs, project tooling, and whether the state description should be part of the type’s public contract—not on line count alone.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Why “revolutionize” should not be read as a performance claim

Project Amber changes how developers express certain Java programs and what the compiler can verify about them. The official material cited here documents language semantics and release history; it does not establish a quantified productivity gain, defect reduction, adoption rate, or runtime-performance improvement. The practical case is clearer models, less routine code, and compile-time checking of exhaustiveness where the type design makes that possible—not a promised benchmark result.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.