1. 简介
Java 16 引入的 records 提供了一种简洁的不可变数据建模方式。它们自动生成构造器、访问器、equals()
、hashCode()
和 toString()
方法,减少了样板代码并提升了可读性。
尽管优势明显,但 records 也存在显著限制:
- 所有字段必须在记录头部声明
- 禁止使用 setter 方法
- 隐式
final
特性限制了扩展性
这些约束使得 records 在需要灵活对象创建的场景(如可选参数或实例修改)中显得力不从心。RecordBuilder 库 提供了优雅的解决方案,通过构建器模式在保持不可变性的同时增强灵活性。
2. 快速开始
使用 RecordBuilder 首先需要添加注解处理器依赖。Maven 配置如下:
<dependency>
<groupId>io.soabase.record-builder</groupId>
<artifactId>record-builder-core</artifactId>
<version>47</version>
</dependency>
配置完成后,在 record 上添加 @RecordBuilder
注解,并可选实现生成的 With
接口以启用内联构建器和流畅的 withX()
方法。
3. 为什么选择 RecordBuilder?
纯 records 的优势显而易见——用最简语法实现不可变数据结构:
public record Person(String name, int age) {}
但全参数构造器和缺乏 setter 使其在需要灵活性时显得僵硬。手动编写构建器 会重新引入 records 旨在消除的样板代码。
RecordBuilder 巧妙地解决了这个矛盾:
✅ 单个注解即可获得流畅、安全、可读的构建和修改方式
✅ 支持分阶段构建器、withX()
方法、自定义钩子等
✅ 完全保持不可变性
例如:
@RecordBuilder
public record Person(String name, int age) implements PersonBuilder.With {}
RecordBuilder 会自动生成完整的构建器方法、withX()
访问器和静态工厂工具,全部遵循不可变性最佳实践。
4. 使用生成的构建器
通过 RecordBuilderDemo
类演示构建器如何提升开发效率:
首先用标准方式创建初始记录:
Person p1 = new Person("foo", 123);
assertEquals("foo", p1.name());
assertEquals(123, p1.age());
通过生成的 withName()
或 withAge()
方法更新字段:
Person p2 = p1.withName("bar");
assertEquals("bar", p2.name());
assertEquals(123, p2.age());
Person p3 = p2.withAge(456);
assertEquals("bar", p3.name());
assertEquals(456, p3.age());
每次转换都保持不可变性,仅修改目标字段。更强大的功能是流畅构建器语法:
Person p4 = p3.with()
.age(101)
.name("baz")
.build();
assertEquals("baz", p4.name());
assertEquals(101, p4.age());
当需要修改多个字段时,这种风格显著提升代码清晰度,避免构造器链式调用,使每个转换意图明确。接下来我们将看到构建器还支持基于 lambda 的内联修改。
5. 高级特性
RecordBuilder 的亮点之一是支持基于 lambda 的内联修改:
Person p5 = p4.with(p -> p.age(200).name("whatever"));
assertEquals("whatever", p5.name());
assertEquals(200, p5.age());
还能在构建器上下文中应用条件逻辑:
Person p6 = p5.with(p -> {
if (p.age() > 13) {
p.name("Teen " + p.name());
} else {
p.name("whatever");
}
});
assertEquals("Teen whatever", p6.name());
assertEquals(200, p6.age());
需要更多控制时,可使用静态构建器工厂(尤其在记录实例外部操作时):
Person p7 = PersonBuilder.from(p6)
.with(p -> p.age(300).name("Manual Copy"));
assertEquals("Manual Copy", p7.name());
assertEquals(300, p7.age());
或直接更新单个字段:
Person p8 = PersonBuilder.from(p6)
.withName("boop");
assertEquals("boop", p8.name());
assertEquals(200, p8.age());
这种静态形式在处理独立构建器、外部数据转换或测试工具时特别有用,实现了构建逻辑与业务逻辑的清晰分离。
6. 自定义与选项
RecordBuilder 不仅生成基础构建器,还提供强大的自定义功能:
- 分阶段构建器:编译时强制必填字段设置,防止运行时错误
- 测试框架集成:轻松创建测试固件或注入部分构建对象
- 架构适配:在不牺牲不可变性的前提下,将构建器逻辑与应用架构对齐
这些特性使 RecordBuilder 成为生产环境中灵活可靠的工具。
7. 对比:RecordBuilder vs 手动构建器
对于字段较少的 record,手动编写构建器看似可行。但随着记录结构演进,手动构建器很快会成为维护噩梦:
- 每次记录变更都需要同步更新构建器
- 新增字段、构造器逻辑、
withX()
方法和build()
逻辑都需要手动调整
RecordBuilder 通过注解处理彻底消除这个负担: ✅ 编译时生成构建器,与记录结构完美同步 ✅ 记录头部的任何修改(增删/重排字段)自动处理 ✅ 消除常见错误源,提升长期可维护性
此外,RecordBuilder 提供的基于 lambda 的更新和静态克隆方法,手动实现需要大量工作。在开发效率、正确性和一致性方面,RecordBuilder 远超手动实现。
8. 结论
RecordBuilder 库让 Java records 的使用更便捷、更实用。它生成流畅且兼容不可变性的构建器,无需手动编写构造器或更新方法。
其核心优势在于平衡性:
- 灵活更新 + 保持不可变性
- 表达力强 + 零样板代码
无论是构建 DTO、API 响应还是配置对象,它都能自然融入开发流程。几乎零配置的情况下,RecordBuilder 成为构建简洁、可扩展 Java 代码的强大工具。
本文完整源码可在 GitHub 获取