Unlock Java 17: Records, Sealed Classes, Pattern Matching & More

This article explains why upgrading from Java 8 to JDK 17 is essential, detailing new language features such as records, sealed classes, pattern matching, text blocks, var inference, enhanced switch, Stream API improvements, better NullPointerExceptions, modern garbage collectors, and the foreign memory access API, all illustrated with concise code examples.

Architect's Tech Stack
Architect's Tech Stack
Architect's Tech Stack
Unlock Java 17: Records, Sealed Classes, Pattern Matching & More

Are you still coding with Java 8? It's time to upgrade to JDK 17, a long‑term support release that brings a suite of powerful language features, making Java code more concise and efficient.

From JDK 8 to JDK 17

Why JDK 17 is a milestone

JDK 17 is the next LTS after JDK 8 and 11, integrating all innovations since JDK 9 and marking a major step in Java modernization.

Significance of LTS

As an LTS version, JDK 17 receives at least eight years of support, allowing enterprises to migrate confidently without frequent upgrades.

Record classes

Problems with traditional JavaBeans

Creating a simple data class traditionally requires boilerplate code for fields, constructors, getters, equals, hashCode, and toString.

public class Person {
    private final String name;
    private final int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() { return name; }
    public int getAge() { return age; }
    @Override
    public boolean equals(Object o) { /* long implementation */ }
    @Override
    public int hashCode() { /* long implementation */ }
    @Override
    public String toString() { return "Person[name=" + name + ", age=" + age + "]"; }
}

Record classes eliminate this boilerplate.

Basic syntax and usage

public record Person(String name, int age) {}

The compiler automatically generates constructor, accessor methods, equals, hashCode, and toString.

Record and immutability

Records are immutable; to modify a value you create a new instance.

Person alice = new Person("Alice", 25);
Person olderAlice = new Person(alice.name(), alice.age() + 1);

When to use records

Ideal for DTOs, value objects, or immutable containers. Not suitable when inheritance, additional fields, or abstract behavior is required.

Sealed classes

Core concept

Sealed classes allow a class to restrict which other classes may extend it, providing a middle ground between final and open inheritance.

public sealed class Shape permits Circle, Rectangle, Triangle {}

permits keyword details

Subclasses must be declared final, sealed, or non‑sealed.

public final class Circle extends Shape { }
public sealed class Rectangle extends Shape permits Square { }
public non-sealed class Triangle extends Shape { }

Combining with interfaces

public sealed interface Vehicle permits Car, Truck, Motorcycle {
    void move();
}

Practical use case

Sealed hierarchies are useful for domain modeling where the set of subtypes is closed.

public sealed interface PaymentMethod permits CreditCard, DebitCard, BankTransfer, DigitalWallet {
    boolean processPayment(double amount);
}
public final class CreditCard implements PaymentMethod {
    @Override
    public boolean processPayment(double amount) { return true; }
}

Pattern matching

Type pattern matching

Before JDK 17, instanceof required a separate cast; JDK 17 allows variable binding directly.

// Old way
if (obj instanceof String) {
    String s = (String) obj;
    if (s.length() > 5) { /* use s */ }
}

// New way
if (obj instanceof String s && s.length() > 5) {
    // use s directly
}

Switch expression enhancements

Switch can now pattern‑match and return a value.

String result = switch (obj) {
    case Integer i -> "Integer: " + i;
    case String s  -> "String: " + s;
    case Person p  -> "Person: " + p.name();
    default        -> "Unknown type";
};

Pattern matching can also improve performance by reducing redundant checks.

Text blocks

Problems with traditional string concatenation

Multi‑line strings required cumbersome concatenation and escaping.

String html = "<html>
" +
    "  <body>
" +
    "    <h1>Hello, World!</h1>
" +
    "  </body>
" +
    "</html>";

Text block syntax

String html = """
    <html>
        <body>
            <h1>Hello, World!</h1>
        </body>
    </html>
    """;

Text blocks preserve line breaks and quotes without escaping.

Formatting tricks

Use \s to keep trailing spaces or backticks to concatenate lines.

String query = """
    SELECT id, name, email \ 
    FROM users \ 
    WHERE status = 'ACTIVE' \ 
    ORDER BY name
    """;

var and enhanced switch

Type inference benefits

var reduces verbosity when the type is evident.

var groupedPeople = new HashMap<String, List<Person>>();

Switch as an expression with yield

String day = switch (dayOfWeek) {
    case 1 -> "Monday";
    case 2 -> "Tuesday";
    case 3 -> "Wednesday";
    case 4 -> "Thursday";
    case 5 -> "Friday";
    case 6, 7 -> "Weekend";
    default -> "Invalid";
};

Complex logic can use a block with yield.

String result = switch (status) {
    case "PENDING" -> {
        log.info("Processing pending");
        yield "Processing";
    }
    case "APPROVED" -> {
        log.info("Processing approved");
        yield "Completed";
    }
    default -> "Unknown";
};

Other useful features

Private interface methods

public interface Logger {
    default void logInfo(String msg) { log(msg, "INFO"); }
    default void logError(String msg) { log(msg, "ERROR"); }
    private void log(String msg, String level) {
        System.out.println("[" + level + "] " + msg);
    }
}

Stream API improvements

List<String> names = people.stream()
    .map(Person::name)
    .filter(name -> name.startsWith("张"))
    .toList();

List<String> words = sentences.stream()
    .mapMulti((s, c) -> {
        for (String w : s.split(" ")) c.accept(w);
    })
    .toList();

Enhanced NullPointerException

JDK 17 reports the exact variable that is null, e.g., "Cannot invoke Person.getName() because 'person' is null".

New garbage collectors

ZGC provides low‑pause (<10 ms) collection for terabyte‑scale heaps.

-XX:+UseZGC

Foreign memory access API

try (MemorySegment segment = MemorySegment.allocateNative(100)) {
    MemoryAccess.setInt(segment, 0, 42);
    int value = MemoryAccess.getInt(segment, 0);
    System.out.println(value); // prints 42
}

These APIs enable safe off‑heap memory manipulation.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaStream APIpattern-matchingSealed ClassesJDK 17recordText Blocks
Architect's Tech Stack
Written by

Architect's Tech Stack

Java backend, microservices, distributed systems, containerized programming, and more.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.