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.
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>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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.