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

Java 记录类与 Lombok 数据类如何选择

来源:17golang原创

时间:2026-10-14 13:12:48 422浏览 收藏

Java 的 record 和 Lombok 的 @Data 都能让数据类少写很多样板代码,但它们解决的不是同一个问题:record 是语言级的“固定数据载体”契约,@Data 是编译期生成 JavaBean 样板成员的工程工具。选择时先问对象是否应该被修改、调用方是否依赖 getXxx()/setXxx(),再看框架和继承约束。

可以先记住一个实用结论:接口请求/响应 DTO、查询结果和只读值对象,优先考虑 record;需要 setter、无参构造、JavaBean 绑定、继承或渐进式改造的对象,优先考虑 Lombok 或普通 class。

record 和 @Data 的最小写法差在哪里

两种写法都可以表达姓名和年龄,但外部看到的 API 完全不同。record 的组件会形成私有 final 字段、同名访问器、规范构造器以及值语义方法;@Data 则默认为非 final 字段生成 getter 和 setter,并生成所需构造器。

// record 表达“创建后按组件读取”的固定数据载体
public record UserView(String name, int age) {
    // 紧凑构造器适合集中放置创建时的参数约束
    public UserView {
        if (age 

UserView 使用 user.name() 读取,不能调用 setter;UserForm 使用 getName()、setName() 等方法。注意 record 是浅层不可变:组件引用本身不能换,但组件若是可变集合,集合内容仍可能变化。

record 与 Lombok Data 的字段、访问器和构造器契约关系说明图
图1:结构说明图,比较 record 与 @Data 对外暴露的成员契约。

先比较五个真正影响选型的维度

维度recordLombok @Data
变化方式组件字段 final,创建后不能重新赋值非 final 字段默认有 setter,可按注解细调
访问器name()、age()getName()、setName()
继承record 隐式 final,不能继承其他类普通 class,可按现有类层次设计
构造器组件决定规范构造器,可用紧凑构造器校验默认生成 required constructor;显式构造器会影响生成规则
工具链编译器直接理解,减少注解处理器依赖需要 Lombok 与 IDE、编译器、构建链协同

因此,“代码更短”不是充分条件。若旧框架通过反射寻找 getName,record 的 name() 可能不满足它;反过来,若对象作为缓存键或跨层传递,随意暴露 setter 会让状态变化变得难以追踪。

按场景做选择:DTO、配置对象和领域实体

接口 DTO 和查询投影:字段通常在边界处一次构造、随后只读,record 能把契约写在类型声明里。序列化框架是否支持 record 要结合项目版本和模块配置确认,不能只因为语言支持就默认所有旧框架都支持。

配置对象和表单对象:如果绑定流程需要先创建空对象、再逐个调用 setter,或者配置系统依赖标准 getter/setter,@Data 更顺手;如果配置是在启动时完整解析并冻结,也可以用 record,并显式提供适配层。

领域实体:实体往往有生命周期、身份和受控变更,不宜因为字段多就直接套 @Data。可以使用普通 class,或者只为不可变快照、值对象使用 record。无论选择哪种方案,都应明确 equals/hashCode 是按全部字段还是按业务身份定义。

DTO 配置对象和领域实体在 record 与 Lombok 之间的选择边界说明图
图2:结构说明图,把常见对象场景映射到选择边界。

迁移时怎样避免“能编译但行为变了”

把旧的 Lombok 数据类改成 record,不只是把类声明换一行。调用方必须把 getter 改成组件访问器,所有修改动作改为重新构造对象;依赖无参构造、反射写字段、代理继承或 JavaBean 命名的框架也要单独确认。

// DTO 转换放在边界层,领域内部只接收已经完成校验的值
public record UserResponse(String name, int age) {
    // 通过工厂方法隐藏外部数据到 DTO 的转换细节
    public static UserResponse from(UserEntity entity) {
        return new UserResponse(entity.getName(), entity.getAge());
    }
}

// 如果旧调用方必须使用 setter,先保留表单模型,不要强行改成 record
@lombok.Data
public class UserCommand {
    private String name;
    private int age;
}

迁移顺序可以是:先把 record 用在新建的边界 DTO;再为旧框架保留 Lombok 表单类;最后通过转换方法隔离两者。这样既能获得不可变对象的清晰契约,也不会把框架适配成本扩散到领域代码。

常见问题

record 是不是绝对不可变?

不是。record 的组件引用不能重新赋值,但引用指向的对象可能仍可变。需要深层不可变时,还要选择不可变集合、拷贝输入或在构造器中完成防御性复制。

@Data 能不能直接替代 record?

不能简单替代。@Data 适合减少普通 class 的 getter、setter、构造器和对象方法样板;record 还表达了 final、组件访问器、不能继承类等语言级约束。

项目已经用了 Lombok,还要引入 record 吗?

可以按边界引入,不需要一次性迁移。新 DTO 和值对象优先使用 record,已有 JavaBean 绑定对象继续使用 Lombok,等调用契约自然收敛后再评估更大范围的改造。

最终的选择规则可以压缩成一句话:对象是“创建后携带一组固定值”,选 record;对象需要被框架逐步填充、需要 setter 或参与既有继承体系,选 Lombok 或普通 class。先确定对象契约,再决定用哪种语法,迁移风险会小得多。

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