Fundamentals 18 min read

Unlock Java 21: Master Record Patterns, Switch Enhancements, and New Templates

This article explains Java 21’s preview features—including record patterns, enhanced switch expressions, virtual threads, string template processors, sequenced collections, and unnamed patterns—showing how they simplify type checks, pattern matching, and code readability with practical code examples.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Unlock Java 21: Master Record Patterns, Switch Enhancements, and New Templates

Record Mode

Record patterns were introduced as a preview in JEP 405 (JDK 19) and refined in JEP 432 (JDK 20). They evolve alongside pattern matching for switch (JEP 441) and interact closely.

1.1 instanceof Type Pattern

<code>Object obj = "Pack"; // before Java 16
if (obj instanceof String) {
    String s = (String) obj;
    System.out.println("强转为String");
}
// from Java 16
if (obj instanceof String s) {
    System.out.println("简便多了");
}</code>

From Java 16 onward, if obj is a String , the pattern matches, the variable s is initialized, and can be used directly.

1.2 Pattern Matching with Records

<code>public record Point(int x, int y) {}
public static void main(String[] args) {
    Object obj = new Point(10, 20);
    if (obj instanceof Point p) {
        int x = p.x();
        int y = p.y();
        System.out.println(x + y);
    }
}</code>

Since Java 21 you can deconstruct a record directly in the pattern:

<code>Object obj = new Point(10, 20);
if (obj instanceof Point(int x, int y)) {
    System.out.println(x + y);
}</code>

1.3 Nested Record Patterns

<code>public record Point(int x, int y) {}
enum Color { RED, GREEN, BLUE }
public record ColoredPoint(Point p, Color c) {}
public record Rectangle(ColoredPoint upperLeft, ColoredPoint lowerRight) {}

Object r = new Rectangle(
    new ColoredPoint(new Point(0, 0), Color.RED),
    new ColoredPoint(new Point(100, 100), Color.BLUE)
);
if (r instanceof Rectangle(ColoredPoint ul, ColoredPoint lr)) {
    System.out.printf("%s, %s%n", ul, lr);
}</code>

Output:

<code>ColoredPoint[p=Point[x=0, y=0], c=RED], ColoredPoint[p=Point[x=100, y=100], c=BLUE]</code>

Further nesting allows extracting inner components:

<code>if (r instanceof Rectangle(
    ColoredPoint(Point(int x, int y), Color c1),
    ColoredPoint lr
)) {
    System.out.printf("x = %d, y = %d%n", x, y);
}</code>

1.4 Cases Where Nested Patterns Do Not Match

<code>public record Pair(Object x, Object y) {}
Pair p = new Pair(42, 42);
if (p instanceof Pair(String s, String t)) {
    System.out.println(s + ", " + t);
} else {
    System.out.println("Not a pair of strings");
}</code>

2. Switch Pattern Matching

The feature started with JEP 406 (JDK 17) and was improved in JEP 420, 427, 433. It works together with record patterns (JEP 440).

Traditional instanceof‑based if‑else

<code>Object obj = 100L;
if (obj instanceof Integer) {
    Integer i = (Integer) obj;
    obj = String.format("int %d", i);
} else if (obj instanceof Long) {
    Long l = (Long) obj;
    obj = String.format("long %d", l);
} else if (obj instanceof String) {
    String s = (String) obj;
    obj = String.format("String %s", s);
}</code>

Switch expression (Java 21)

<code>var ret = switch (obj) {
    case Integer i -> String.format("int %d", i);
    case Long l    -> String.format("long %d", l);
    case String s  -> String.format("String %s", s);
    default        -> obj.toString();
};
System.out.printf("result ret = %s%n", ret);</code>

2.1 Switch with null values

<code>String s = null;
switch (s) {
    case null -> System.out.println("oops");
    case "a", "b" -> System.out.println("a or b");
    default -> System.out.println("default value");
}</code>

Image illustrating null handling:

2.2 Guarded case clauses

<code>static void fn1(String resp) {
    switch (resp) {
        case String s -> {
            if (s.equalsIgnoreCase("success"))
                System.out.println("处理成功");
            else if (s.equalsIgnoreCase("failure"))
                System.err.println("处理失败");
            else
                System.out.println("未知结果");
        }
    }
}

static void fn2(String resp) {
    switch (resp) {
        case null -> {}
        case String s when s.equalsIgnoreCase("success") -> System.out.println("处理成功");
        case String s when s.equalsIgnoreCase("failure") -> System.err.println("处理失败");
        case String s -> System.out.println("未知结果");
    }
}</code>

2.3 Switch with enum constants

<code>public enum Color { RED, BLUE, GREEN }
public static void fn1(Color c) {
    switch (c) {
        case RED, BLUE -> System.out.println("我喜欢的颜色");
        case GREEN -> { /* TODO */ }
        default -> System.out.println("我讨厌的颜色");
    }
}</code>

3. Virtual Threads

See the linked article “JDK 21 Virtual Threads” for details (preview feature).

4. String Templates

4.1 STR Processor (preview)

<code>String firstName = "Bill";
String lastName  = "Duck";
String fullName  = STR."{firstName} {lastName}";
System.out.println(fullName); // Bill Duck

int x = 10, y = 20;
String result = STR."{x} + {y} = {x + y}";
System.out.println(result); // 10 + 20 = 30
</code>

4.2 FMT Processor (preview)

<code>record Rectangle(String name, double width, double height) {
    double area() { return width * height; }
}
public static void main(String[] args) {
    Rectangle[] zone = {
        new Rectangle("Alfa", 17.8, 31.4),
        new Rectangle("Bravo", 9.6, 12.4)
    };
    String s = FMT."""
        Description     Width    Height     Area
        %-12s{zone[0].name}  %7.2f{zone[0].width}  %7.2f{zone[0].height}     %7.2f{zone[0].area()}
        %-12s{zone[1].name}  %7.2f{zone[1].width}  %7.2f{zone[1].height}     %7.2f{zone[1].area()}
        {"-".repeat(28)} Total %7.2f{zone[0].area() + zone[1].area()}
    """;
    System.out.println(s);
}
</code>

5. Sequenced Collections (JDK 21)

Three new interfaces provide first/last element access and reversal:

<code>public interface SequencedCollection<E> extends Collection<E> {
    SequencedCollection<E> reversed();
    default void addFirst(E e) {}
    default void addLast(E e) {}
    default E getFirst() { return null; }
    default E getLast() { return null; }
    default E removeFirst() { return null; }
    default E removeLast() { return null; }
}

public interface SequencedSet<E> extends SequencedCollection<E>, Set<E> {
    SequencedSet<E> reversed();
}

public interface SequencedMap<K,V> extends Map<K,V> {
    SequencedMap<K,V> reversed();
    default Map.Entry<K,V> firstEntry() { return null; }
    default Map.Entry<K,V> lastEntry() { return null; }
    // other default methods …
}
</code>

These interfaces are now super‑interfaces of List , Deque , LinkedHashSet , SortedSet , LinkedHashMap , and SortedMap . Diagram omitted.

6. Unnamed Patterns & Variables (preview)

<code>if (obj instanceof Rectangle(ColoredPoint(Point(int x, int y), _))) {
    System.out.printf("x = %d, y = %d%n", x, y);
}

int[] arr = {1,2,3,4,5};
int total = 0;
for (var _ : arr) {
    total++;
}
</code>

7. Unnamed Classes & Main Method (preview)

<code>public class UnnamedClassAndMain {
    void main() {
        System.out.println("Hello World!!!");
    }
}

// Even more minimal form
void main() {
    System.out.println("Hello World!!!");
}
</code>
JavaJDK21String TemplatesRecord PatternsSwitch Pattern MatchingSequenced Collections
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login 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.