登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  java教程

Java record 模式匹配的嵌套字段提取

来源:17golang原创

时间:2026-10-28 11:34:18 475浏览 收藏

当一个 Java 对象由多层 record 组成时,传统写法往往要先判断外层类型,再逐层调用 accessor:order.customer().address().city()。Java 21 起可以用嵌套 record pattern 把类型判断和字段提取合并到一个条件里,适合“对象结构稳定、读取路径明确”的代码。

官方地址:https://docs.oracle.com/en/java/javase/25/language/record-patterns.html

核心写法是把内层 record pattern 放进外层组件位置:Order(var orderId, Customer(var name, Address(var city)))。只有每一层都匹配成功,最内层变量才会进入条件体的作用域。

先把对象图和模式括号对齐

先用一个三层订单模型说明组件顺序。record pattern 的括号不是任意字段名列表,而是严格对应 record 声明中的组件顺序:

// 三层 record 的组件顺序决定模式中的括号顺序
record Address(String city) {}
record Customer(String name, Address address) {}
record Order(String orderId, Customer customer) {}

// Order 的第二个组件是 Customer,Customer 的第二个组件是 Address
Order order = new Order(
    "A-1001",
    new Customer("林默", new Address("杭州"))
);

因此,外层的两个位置分别对应 orderId 和 customer;Customer 的两个位置对应 name 和 address;Address 只有一个位置,对应 city。先画清这棵对象树,再写模式,最不容易把字段顺序写错。

Order、Customer、Address 三层 record 与嵌套模式变量的结构关系说明图
图1:嵌套 record pattern 按组件顺序递归展开的结构说明图,不是运行截图。

一条 instanceof 直接取出嵌套字段

最小可用写法如下。示例使用 var 让编译器根据每个 record 组件推断模式变量类型;它不会改变匹配规则,也不会跳过类型检查。

static String describe(Object value) {
    // 每一层模式都要匹配,成功后才能使用三个绑定变量
    if (value instanceof Order(
            var orderId,
            Customer(var name, Address(var city)))) {
        return orderId + " / " + name + " / " + city;
    }

    // 外层类型或任一嵌套 record 不匹配时,统一返回未识别
    return "未识别的订单结构";
}

这里的收益不是少写几个方法调用,而是把“我只接受这棵对象结构”的边界放进条件本身。匹配成功后,orderId、name 和 city 都已经是当前分支内可用的局部变量;不需要先声明一个宽泛的 Object 再手动转换。

null 和类型不匹配时为什么是 false

Oracle 文档明确指出,null 不匹配任何 record pattern。这个规则会递归影响嵌套结构:value 是 null,或者 Order.customer()、Customer.address() 其中一层是 null,上面的条件都不会进入方法体。

static String cityOrFallback(Order order) {
    // 内层 address 为 null 时,嵌套模式整体不匹配
    if (order instanceof Order(var id, Customer(var name, Address(var city)))) {
        return id + ":" + city;
    }

    // 需要区分“客户为空”和“地址为空”时,保留显式 accessor 判断
    if (order == null || order.customer() == null) {
        return "缺少客户信息";
    }
    if (order.customer().address() == null) {
        return "缺少地址信息";
    }
    return "结构存在,但字段规则需要单独处理";
}

这也是选型边界:只需要“完整结构才处理”时,嵌套模式很紧凑;如果不同缺失层级需要不同提示或补偿策略,先用 accessor 分层判断反而更清楚。

嵌套 record pattern 对象类型、null 边界和匹配结果的关系图
图2:record pattern 的类型与 null 匹配边界关系图,强调失败分支而非运行结果。

var、泛型和变量作用域的三个边界

第一,var 只负责推断。在 Order(var id, Customer(var name, Address(var city))) 中,变量类型来自各个组件的静态类型;如果需要更强的可读性,也可以写成 Order(String id, Customer(String name, Address(String city)))。

第二,泛型要避免原始类型。对于 Envelope,可以让类型参数从已知的接收变量中推断:

record Profile(String name) {}
record Envelope(T data) {}

static String profileName(Envelope envelope) {
    // 已知 Envelope,内层类型可以安全推断为 Profile
    if (envelope instanceof Envelope(Profile(var name))) {
        return name;
    }
    return "没有匹配到 Profile";
}

如果先把对象退化成原始的 Envelope,再尝试匹配 Envelope,编译器可能拒绝不安全的 unchecked conversion。不要用原始类型绕过边界。

第三,变量名不能重复声明。模式列表中的每个绑定变量都必须有自己的名字;如果要比较两个位置是否相等,应先分别绑定,再在条件后面写普通比较,例如 if (value instanceof Pair(var left, var right) && left.equals(right))。

嵌套模式、显式 accessor 和 switch 怎么选

场景建议原因
结构固定,只读取一条路径嵌套 record pattern类型边界与字段绑定集中在一个条件中
每一层缺失都要单独提示显式 accessor可以按层返回不同错误或补偿结果
多个 record 类型要分支处理switch pattern把不同结构的结果分到各个 case

如果改用 switch,要记得 record pattern 本身不匹配 null;当选择器可能为空时,应显式设计 case null。不要为了追求一行代码,把复杂业务校验全部塞进很深的模式括号。

常见问题

record pattern 是不是只适用于 record 的第一层字段?

不是。只要内层组件本身也是可匹配的 record,就可以继续嵌套;每一层的模式列表仍必须和对应 record 的组件数量、顺序对齐。

嵌套模式能不能把 null 自动变成默认值?

不能。null 会让对应 record pattern 不匹配。需要默认值时,应在模式失败后写明确的补偿分支,或者先用 accessor 读取并处理 null。

总结一下:把嵌套 record 当成一棵有固定组件顺序的对象树,外层括号对应外层组件,内层括号继续展开;稳定结构用模式一次提取,分层错误处理则保留显式 accessor。这样既能减少样板代码,也不会把 null 和泛型边界藏起来。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>