CodeGym /课程 /JAVA 25 SELF /Record Patterns (Java 21+): 语法与示例

Record Patterns (Java 21+): 语法与示例

JAVA 25 SELF
第 65 级 , 课程 3
可用

1. 深入了解 record 类

在深入 record patterns 之前,先回顾一下什么是 record 类。

Record 是一种简洁、不可变的类,会自动生成 equalshashCodetoString 方法,并为所有组件自动创建 getter。Record 类在 Java 16 引入,对厌倦了手写 DTO 的人来说可谓大礼包。

// 经典示例:具有两个字段的 Point
record Point(int x, int y) {}

创建对象并访问字段:

Point p = new Point(10, 20);
System.out.println(p.x()); // 10
System.out.println(p.y()); // 20
System.out.println(p);     // Point[x=10, y=20]

为什么需要 Record Patterns

在 Java 21 之前,如果你想检查某个对象是否为某个 record,并获取其字段,就得写大量样板代码:通过 instanceof 检查类型、进行强制类型转换并调用 getter。

Object obj = new Point(5, 7);
if (obj instanceof Point) {
    Point p = (Point) obj;
    int x = p.x();
    int y = p.y();
    System.out.println("x=" + x + ", y=" + y);
}

如果 record 很复杂或是嵌套的,代码很快会变成“解包森林”。而且此前在 switch 中进行这种识别也并不方便。

Record patterns 允许在模式匹配中直接解构 record 类的值:既可以在 instanceof,也可以在 switch 中。这样代码更短、更安全也更清晰。

2. Record patterns 语法:最简单的示例

record Point(int x, int y) {}

Object obj = new Point(10, 20);

if (obj instanceof Point(int x, int y)) {
    System.out.println("x=" + x + ", y=" + y);
}

这里发生了什么?

  • obj instanceof Point(int x, int y) —— 同时完成类型检查,并将组件立即解构到变量 xy 中。
  • 变量 xy 只在 if 块内部可用。

结果:

x=10, y=20

注意:如果 obj 不是 Point 或其为 null,则 if 块不会执行,这些变量也不会被声明。

3. 在 switch 中使用 record patterns:简洁且安全

record Point(int x, int y) {}
record Circle(Point center, int radius) {}

Object shape = new Point(3, 4);

switch (shape) {
    case Point(int x, int y) -> System.out.println("Point: x=" + x + ", y=" + y);
    case Circle(Point center, int r) -> System.out.println("Circle with radius " + r);
    default -> System.out.println("Unknown shape!");
}
  • case Point(int x, int y) 中,我们可以直接使用 xy
  • case Circle(Point center, int r) 中,可用的变量是 centerr

结果:

Point: x=3, y=4

配合 sealed 继承层次,switch 可以做到穷尽匹配:如果遗漏某个分支,编译器会给出警告。

4. 嵌套的 record patterns:在解构中继续解构

record Point(int x, int y) {}
record Line(Point start, Point end) {}

Object obj = new Line(new Point(1, 2), new Point(3, 4));

if (obj instanceof Line(Point(int x1, int y1), Point(int x2, int y2))) {
    System.out.println("Line from (" + x1 + "," + y1 + ") to (" + x2 + "," + y2 + ")");
}

结果:

Line from (1,2) to (3,4)

需要处理树、图或复杂结构吗?嵌套模式会是你的好伙伴。

5. 带守卫表达式(when)的 record patterns

Object obj = new Point(0, 100);

if (obj instanceof Point(int x, int y) && x == 0) {
    System.out.println("点在 Y 轴上:y=" + y);
}

在 switch 中则通过 when 来实现:

switch (obj) {
    case Point(int x, int y) when x == 0 -> System.out.println("在 Y 轴上:y=" + y);
    case Point(int x, int y) when y == 0 -> System.out.println("在 X 轴上:x=" + x);
    case Point(int x, int y) -> System.out.println("普通点");
    default -> System.out.println("不是点");
}

6. 在真实应用中使用 record patterns

来一个实用示例——几何图形。

record Point(int x, int y) {}
record Circle(Point center, int radius) {}
record Rectangle(Point topLeft, int width, int height) {}
public static void printShapeInfo(Object shape) {
    switch (shape) {
        case Point(int x, int y) -> System.out.println("Point: (" + x + ", " + y + ")");
        case Circle(Point(int x, int y), int radius) ->
            System.out.println("Circle: center=(" + x + ", " + y + "), radius=" + radius);
        case Rectangle(Point(int x, int y), int width, int height) ->
            System.out.println("Rectangle: topLeft=(" + x + ", " + y + "), size=" + width + "x" + height);
        default -> System.out.println("Unknown shape");
    }
}

调用示例:

printShapeInfo(new Rectangle(new Point(5, 10), 20, 30));

结果:

Rectangle: topLeft=(5, 10), size=20x30

7. 使用 record patterns 的限制和细节

仅适用于 record 类。 Record patterns 只适用于真正的 record 类对象。

class NotARecord {
    int a, b;
    NotARecord(int a, int b) { this.a = a; this.b = b; }
}

// 错误!这不是 record
// if (obj instanceof NotARecord(int a, int b)) { ... }

组件的数量与类型必须匹配。 模式必须与 record 类的组件一一对应。

record Pair(int a, String b) {}

// 错误:类型不匹配
// if (obj instanceof Pair(String a, int b)) { ... }

变量仅在块内可见。 在模式中声明的名称,仅在匹配成功的特定 ifcase 的主体中可见。

8. 嵌套模式:树结构示例

sealed interface Expr permits NumberExpr, PlusExpr, MinusExpr {}

record NumberExpr(int value) implements Expr {}
record PlusExpr(Expr left, Expr right) implements Expr {}
record MinusExpr(Expr left, Expr right) implements Expr {}
int eval(Expr expr) {
    return switch (expr) {
        case NumberExpr(int value) -> value;
        case PlusExpr(Expr left, Expr right) -> eval(left) + eval(right);
        case MinusExpr(Expr left, Expr right) -> eval(left) - eval(right);
    };
}

使用示例:

Expr e = new PlusExpr(new NumberExpr(7), new MinusExpr(new NumberExpr(10), new NumberExpr(3)));
System.out.println(eval(e)); // 7 + (10 - 3) = 14

这里我们直接解构 NumberExpr(int value)PlusExpr(Expr left, Expr right)MinusExpr(Expr left, Expr right)——代码因此更简洁、更具表现力。

9. 表格:使用与不使用 record patterns 的模式匹配对比

方式 手动类型转换 使用 record patterns 的模式匹配
类型检查
if (obj instanceof ...)
if (obj instanceof Point(int x, int y))
类型转换
(Point) obj
不需要
访问字段
p.x(), p.y()
x, y
嵌套解构 大量手写代码 嵌套模式
在 switch 中使用 不便/不可行 简单、简洁

10. 使用 record patterns 的常见错误

错误 #1:试图对非 record 类使用 record pattern。 如果你写的是普通类,却试图把它“解构”为 record,编译器不会允许。Record patterns 只对真正的 record 类生效。

错误 #2:组件数量或类型不匹配。 模式必须完全匹配 record 的组件签名。例如,对于 record Pair(int a, String b) 不能写 Pair(int x, int y)

错误 #3:尝试在块之外使用模式变量。 模式中声明的名称仅在对应的 if/case 块内可用;离开该块后,这些变量不存在。

错误 #4:在旧版 JDK 上使用 record patterns。 支持是在 Java 21 中加入的。在 Java 17 及以下版本会得到语法错误。请检查 JDK 版本以及 IDE 的支持情况。

错误 #5:过度深的嵌套模式。 嵌套能力很强,但不要不必要地复杂化:过深的结构会降低可读性并提高出错风险。

评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION