
本文介绍一种比链式 `tobuilder()` 更清晰、更易读的方式,利用 lombok 的 `@with` 注解实现嵌套 dto 的深层不可变修改,避免多层 builder 嵌套,提升代码可维护性。
Lombok 的 @Builder(toBuilder = true) 虽然支持构建器模式,但在处理多层嵌套对象(如 Example → ExampleTwo → SomeData)时,逐层调用 .toBuilder().xxx(...).build() 会导致代码冗长、嵌套过深、可读性下降。此时,@With 是更优雅的替代方案。
@With 为每个字段自动生成 withXxx(...) 方法,返回新实例(即不可变式深拷贝),语义清晰、链式自然,且天然支持嵌套调用:
// ✅ 推荐:简洁、扁平、语义明确
Example duplicate = example
.withExampleTwo(
example.getExampleTwo()
.withSomeData(
example.getExampleTwo().getSomeData()
.withSpecialId("SPE_1")
)
);⚠️ 注意:@With 默认仅对非 final 字段生成方法(除非配合 @Wither 或显式启用 lombok.with.flag = true)。确保你的字段未被 final 修饰,或在 lombok.config 中添加:lombok.with.flag = true
要启用 @With,需为所有参与链式更新的类显式添加该注解:
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@With // ← 新增:为 Example 启用 with 方法
public class Example {
private ExampleTwo exampleTwo;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@With // ← 新增:为嵌套类启用
public static class ExampleTwo {
private SomeData someData;
private AnotherField anotherField;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@With // ← 新增:为最内层数据类启用
public class SomeData {
private String specialId; // 注意:字段名应为 specialId(非 specialID),否则 withSpecialId() 不生效
private String firstName;
private String lastName;
}✅ 优势总结:
- 零额外依赖:纯 Lombok 功能,无需引入 MapStruct、Dozer 或 Jackson;
- 类型安全 & 编译期检查:方法名由字段名推导,拼写错误立即报错;
- 不可变友好:每次 withXxx() 返回新对象,天然契合函数式风格;
- 可读性强:逻辑流向自顶向下,一目了然,符合“意图优先”编码原则。
? 小技巧:若字段命名不规范(如 specialID),Lombok 默认生成 withSpecialID(),但 Java Bean 规范建议使用 specialId(驼峰小写首字母)。建议统一字段命名,避免因大小写导致 withXxx() 方法未生成。
综上,当面对多层嵌套对象的深拷贝+局部更新场景时,@With 是比 toBuilder() 更轻量、更直观、更符合表达意图的解决方案。










