⭐ 推荐:社区规则条款 V1.0

Java 记录类 record 和 Lombok 该怎么选?

域名注册
域名注册 版主萌新站长 👑黑卡会员
发布于 2026-10-16 02:17 ·1 浏览 ·0 回复
内容摘要

JDK 16 及以上且只需不可变数据载体时优先用 record,JDK 8/11、需要可变对象或 JPA 实体时继续用 Lombok,二者可组合使用如 record 搭配 @Builder。

**结论先行:JDK 16 及以上、并且这个类只是"不可变数据载体",直接用 record,不要引入 Lombok;JDK 8/11 项目、需要可变对象或 JPA 实体,继续用 Lombok;两者并不互斥,record + Lombok @Builder 是常见组合。**

record 和 Lombok 的核心区别是什么?

结论:record 是语言级特性,编译期就确定了语义;Lombok 是编译期注解处理器,本质是"帮你生成代码"。

record 在 Java 14 作为预览特性(JEP 359)出现,Java 15 二次预览(JEP 384),Java 16 正式转正(JEP 395,2021 年 3 月发布)。它隐式继承 java.lang.Record,组件字段全部是 private final,自动生成全参构造器、访问器、equals、hashCode、toString。注意访问器是 name() 而不是 getName(),这是最容易踩的一脚。

Lombok 的 @Value 也能做到"final 字段 + 全参构造 + equals/hashCode/toString",但生成的是 JavaBean 风格的 getName(),并且需要 IDE 装插件、构建配置 annotationProcessor。一句话区分:**record 是不可变数据类型的官方语法,Lombok 是给普通类批量生成样板代码的工具。**

什么情况下应该用 record?

结论:JDK ≥ 16、纯数据承载、不需要 setter、不需要继承别的类,就选 record。

典型场景:DTO、API 返回值、方法内部的多返回值、Map 的复合 key、事件对象。写起来极短:

public record Point(int x, int y) {
    public Point {                    // 紧凑构造器,做校验
        if (x < 0) throw new IllegalArgumentException("x < 0");
    }
}

生态兼容性上:Jackson 从 2.12(2020 年 11 月)开始原生支持 record 的序列化与反序列化;MyBatis 由于 record 没有无参构造和 setter,必须用 resultMap 的 <constructor> 做构造器映射。另外 record 是 final 的,不能继承、也不能被继承,但可以实现接口。

什么情况下仍然要用 Lombok?

结论:受限于 JDK 版本、需要可变状态、或者框架强制要求 JavaBean 规范时,Lombok 仍是唯一选择。

三类硬约束:

  1. JPA 实体不能用 record。JPA 规范要求实体有 public/protected 无参构造器和非 final 字段,Hibernate 要能通过反射设值,record 两条都不满足。
  2. 需要 setter 的场景。record 没有 setter,要"改一个字段"只能 new 一个新对象(Lombok 有 @With,record 没有等价物)。
  3. JDK 版本卡住。还在 JDK 8 或 11 的项目,record 根本不存在,只能靠 Lombok。

另外提醒一个版本坑:Lombok 对 JDK 版本很敏感,JDK 21 需要 Lombok 1.18.30 及以上,升级 JDK 时忘升 Lombok 会直接编译失败报 java.lang.NoSuchFieldError: com.sun.tools.javac.tree.JCTree$JCImport。record 则没有这个问题,它是 JDK 自带的。

record 和 Lombok 能一起用吗?

结论:能,而且很实用。record 负责不可变语义,Lombok 负责补 Builder。

较新版本的 Lombok(1.18.20 起)已支持在 record 上使用 @Builder:

@Builder
public record UserDTO(Long id, String name, List<String> roles) {}

这样字段多的 DTO 就能用 UserDTO.builder().id(1L).name("a").build() 构造,可读性比一堆位置参数强得多。建议在 record 的紧凑构造器里对集合做 List.copyOf(roles),否则外部仍能改到这个 List,record 只保证"引用不可变",不保证"内容不可变"——这是浅不可变最常见的误解。

迁移到 record 时要注意哪些坑?

结论:三个高频问题——访问器命名、序列化机制、工具链假设。

第一,getName() 变 name(),任何反射、SpEL 表达式、模板引擎访问属性的代码都要检查。第二,record 的序列化走 JDK 专用路径,不能自定义 writeObject/readObject,反序列化直接调规范构造器,所以构造器里的校验在反序列化时同样生效(这其实是好事,但校验失败会抛异常)。第三,一些老库默认"有 setter 就能填值",遇到 record 会静默失败,升级前先在测试里跑一遍。

收个尾:**新项目、JDK 16+、数据不可变——record;存量项目、可变对象、JPA 实体——Lombok;字段多又想不可变——record + @Builder。** 判断标准不是"哪个更时髦",而是"这个类需不需要被修改、被继承、被框架反射赋值"。

本文转载自 GJ站长论坛,原文地址:https://www.gj0.com/thread-2152.html
转载请注明出处,版权归原作者所有。
—/ 5暂无评分
还没有评分,来打第一分吧

全部回复 0

还没有回复,来抢沙发~