Java 21 记录模式与模式匹配实战:用更简洁的代码处理复杂数据结构

2026-06-06 0 270

从 Java 14 开始引入的模式匹配,在 Java 21 中迎来了里程碑式的提升——记录模式(Record Patterns)正式转正。这项特性让开发者能够直接在 instanceofswitch 中解构记录对象,将类型检查、类型转换和字段提取合并为一步操作。结合 密封类(Sealed Classes) 和增强的 switch 表达式,Java 现在能够以函数式编程风格清晰表达复杂的数据驱动逻辑。本文将从基础语法讲起,通过三个完整的实战案例,带你彻底掌握这套简化代码的利器。

一、模式匹配的演进:从传统 instanceof 到记录模式

在传统的 Java 代码中,我们经常需要编写“检查类型 → 强制转换 → 使用变量”的样板代码,例如:

// 传统写法
if (obj instanceof String) {
    String s = (String) obj;
    System.out.println(s.length());
}

Java 16 引入了 instanceof 模式匹配,将转换与绑定合并:

// Java 16 之后
if (obj instanceof String s) {
    System.out.println(s.length());
}

Java 17 将该模式扩展到了 switch 表达式,实现了更简洁的多分支类型判断。而 Java 21 的 记录模式 更进一步:如果对象是记录(record),可以直接在模式中提取其组件值。

record Point(int x, int y) {}

// Java 21 记录模式
void printSum(Object obj) {
    if (obj instanceof Point(int x, int y)) {
        System.out.println(x + y);
    }
}

这种能力让开发者无需手动调用 point.x(),模式匹配自动完成解构,代码既简洁又安全。

二、记录模式的基础语法与嵌套解构

记录模式不仅适用于单层记录,还可以嵌套使用,轻松处理树形结构。例如定义一个包含线段的记录:

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

// 嵌套记录模式
void printCoordinates(Object obj) {
    if (obj instanceof Line(Point(int x1, int y1), Point(int x2, int y2))) {
        System.out.printf("线段从 (%d,%d) 到 (%d,%d)%n", x1, y1, x2, y2);
    }
}

在这个例子中,Line 被解构成两个 Point,而每个 Point 又被解构成 xy,一步到位。如果没有记录模式,我们需要写多层 instanceof 和多行获取组件的方法调用。

switch 中使用记录模式同样直观:

String describe(Object obj) {
    return switch (obj) {
        case Point(int x, int y) when x == 0 && y == 0 -> "原点";
        case Point(int x, int y) when x == y -> "对角线上的点";
        case Point(var x, var y) -> "点(" + x + "," + y + ")";
        default -> "未知类型";
    };
}

这里使用了 when 子句进行附加条件约束,并且可以用 var 自动推断组件类型。

三、实战案例一:几何图形面积计算器

我们使用记录和密封类构建一个几何形状体系,然后利用 switch 模式匹配实现计算面积。首先定义密封接口和各形状记录:

sealed interface Shape permits Circle, Rectangle, Triangle {}
record Circle(double radius) implements Shape {}
record Rectangle(double width, double height) implements Shape {}
record Triangle(double base, double height) implements Shape {}

现在编写面积计算函数,无需任何 if-else 或 visitor 模式:

double area(Shape shape) {
    return switch (shape) {
        case Circle(double r) -> Math.PI * r * r;
        case Rectangle(double w, double h) -> w * h;
        case Triangle(double b, double h) -> 0.5 * b * h;
    };
}

当新增形状时(例如添加 Square 记录),编译器会强制我们在 switch 中处理新分支,因为密封类限定了所有子类,确保了类型安全性。这是传统多态难以做到的。

四、实战案例二:处理 JSON 节点并格式化输出

在实际开发中,我们经常需要处理树形的 JSON 结构。利用记录模式和 switch,可以轻松编写一个递归的 JSON 格式化器。先定义 JSON 节点类型:

sealed interface JsonNode permits JsonString, JsonNumber, JsonObject, JsonArray {}
record JsonString(String value) implements JsonNode {}
record JsonNumber(double value) implements JsonNode {}
record JsonObject(Map<String, JsonNode> entries) implements JsonNode {}
record JsonArray(List<JsonNode> elements) implements JsonNode {}

递归打印 JSON 字符串的实现:

String format(JsonNode node) {
    return switch (node) {
        case JsonString(String s) -> """ + s + """;
        case JsonNumber(double d) -> String.valueOf(d);
        case JsonObject(Map<String, JsonNode> map) -> {
            StringBuilder sb = new StringBuilder("{");
            map.forEach((k, v) -> sb.append(""").append(k).append("":")
                                     .append(format(v)).append(","));
            if (!map.isEmpty()) sb.deleteCharAt(sb.length() - 1);
            sb.append("}");
            yield sb.toString();
        }
        case JsonArray(List<JsonNode> list) -> {
            String elements = list.stream()
                                  .map(this::format)
                                  .collect(Collectors.joining(","));
            yield "[" + elements + "]";
        }
    };
}

代码中没有 instanceof 和强制转换,每个分支直接获得了解构后的数据。这比使用访问者模式或一系列的 if-else 链要清晰得多。

五、实战案例三:简化抽象语法树(AST)操作

在编译器或规则引擎中,AST 节点通常也很适合用记录表示。我们以简单的算术表达式为例:

sealed interface Expr permits NumberExpr, AddExpr, MulExpr {}
record NumberExpr(int value) implements Expr {}
record AddExpr(Expr left, Expr right) implements Expr {}
record MulExpr(Expr left, Expr right) implements Expr {}

利用模式匹配递归计算表达式值:

int evaluate(Expr expr) {
    return switch (expr) {
        case NumberExpr(int v) -> v;
        case AddExpr(Expr left, Expr right) -> evaluate(left) + evaluate(right);
        case MulExpr(Expr left, Expr right) -> evaluate(left) * evaluate(right);
    };
}

遍历表达式树时,记录模式和 switch 的组合让代码极度简洁。如果未来添加 SubExpr,代码会因密封类的穷尽性产生编译错误,避免遗漏。

六、模式匹配带来的性能优势

除了可读性,记录模式和解构还有性能上的好处。在传统的 instance-cast 代码中,JVM 需要执行类型检查,然后再检查记录组件是否匹配。随着模式匹配的成熟,JIT 编译器可以生成专门的匹配代码路径,减少分支预测失败。此外,在一次模式匹配中完成的多个条件检查会被优化为单一操作,避免了重复的 instanceof

七、总结与最佳实践

  • 优先使用记录:对于不可变的数据载体,record 比普通类更简洁,且天然支持模式匹配。
  • 密封类 + switch 穷尽性:用密封类限定子类型,确保 switch 分支完整,编译器会帮你检查遗漏。
  • 避免过度嵌套:虽然记录模式支持深层嵌套,但太长的模式会降低可读性,可适时拆分为子方法。
  • 谨慎使用 var:在模式中使用 var 可以简化书写,但显式类型能增加代码的自文档化程度。

Java 21 的记录模式和模式匹配不仅仅是语法糖,它们代表了 Java 向更声明式、更类型安全的编程范式的转变。通过本文的三个实战案例,你应该已经感受到这种转变带来的巨大生产力提升。下一次当你面对复杂的类型判断或数据解构时,不妨尝试用记录模式来重写,你的代码会因此变得更加优雅。

Java 21 记录模式与模式匹配实战:用更简洁的代码处理复杂数据结构
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

版权声明:
本站资源有的来自互联网收集整理,本站纯免费分享提供学习使用,如果侵犯了您的合法权益,请联系本站我们会及时删除。
本站资源仅供研究、学习交流之用,免费开源项目不代表完全可商用,若商业用途请先咨询开发企业能否商用,否则产生的一切后果将由下载用户自行承担。
原创板块未经允许不得转载,否则将追究法律责任。

淘吗网 java Java 21 记录模式与模式匹配实战:用更简洁的代码处理复杂数据结构 https://www.taomawang.com/server/java/2099.html

常见问题

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务