1. 概述
Lombok 提供了一个非常优雅的方式实现 Builder 模式 —— 只需一个注解 @Builder
,就可以告别冗长的样板代码。
但在实际开发中,如果你的类涉及继承关系,直接使用 @Builder
就会踩坑。本文将带你了解两种解决方式:
- 一种是使用标准 Lombok 功能手动构造;
- 另一种是借助 Lombok 1.18 引入的实验性特性
@SuperBuilder
。
⚠️ 注意:如果你对 Builder 模式还不熟悉,可以先参考 Lombok @Builder 使用指南 和 Project Lombok 简介。
2. Lombok @Builder
与继承
2.1. 问题场景
我们来看一个典型的继承结构:
@Getter
@AllArgsConstructor
public class Parent {
private final String parentName;
private final int parentAge;
}
@Getter
@Builder
public class Child extends Parent {
private final String childName;
private final int childAge;
}
此时,IDE 会报错:
Implicit super constructor Parent() is undefined. Must explicitly invoke another constructor
原因很简单:Lombok 的 @Builder
注解只会处理当前类的字段,不会自动识别父类字段。也就是说,它默认调用了无参构造函数,而 Parent
类只提供了全参构造函数。
2.2. 解决方案:手动添加构造函数
解决这个问题其实很简单粗暴:手动添加一个包含父类字段的构造函数,并在该构造函数上打上 @Builder
注解。
@Getter
@AllArgsConstructor
public class Parent {
private final String parentName;
private final int parentAge;
}
@Getter
public class Child extends Parent {
private final String childName;
private final int childAge;
@Builder
public Child(String parentName, int parentAge, String childName, int childAge) {
super(parentName, parentAge);
this.childName = childName;
this.childAge = childAge;
}
}
这样就能愉快地使用 Builder 了:
Child child = Child.builder()
.parentName("Andrea")
.parentAge(38)
.childName("Emma")
.childAge(6)
.build();
assertThat(child.getParentName()).isEqualTo("Andrea");
assertThat(child.getParentAge()).isEqualTo(38);
assertThat(child.getChildName()).isEqualTo("Emma");
assertThat(child.getChildAge()).isEqualTo(6);
✅ 这个方法虽然需要手动写构造函数,但兼容性好,适用于任何版本的 Lombok。
2.3. 多个 @Builder
冲突处理
如果父类本身也用了 @Builder
,那就会出现方法冲突:
The return type is incompatible with Parent.builder()
这是因为两个类都生成了同名的 builder()
方法。解决办法是给其中一个指定不同的 builder 方法名:
@Getter
public class Child extends Parent {
private final String childName;
private final int childAge;
@Builder(builderMethodName = "childBuilder")
public Child(String parentName, int parentAge, String childName, int childAge) {
super(parentName, parentAge);
this.childName = childName;
this.childAge = childAge;
}
}
现在你可以通过以下方式分别调用:
Child.builder()
→ 调用父类的 builderChild.childBuilder()
→ 调用子类的 builder
2.4. 支持更深层的继承结构
对于三层甚至更多层的继承结构,也可以沿用这个套路。
比如再加一个 Student
继承自 Child
:
@Getter
public class Student extends Child {
private final String schoolName;
@Builder(builderMethodName = "studentBuilder")
public Student(String parentName, int parentAge, String childName, int childAge, String schoolName) {
super(parentName, parentAge, childName, childAge);
this.schoolName = schoolName;
}
}
使用时:
Student student = Student.studentBuilder()
.parentName("Andrea")
.parentAge(38)
.childName("Emma")
.childAge(6)
.schoolName("Baeldung High School")
.build();
assertThat(student.getChildName()).isEqualTo("Emma");
assertThat(student.getChildAge()).isEqualTo(6);
assertThat(student.getParentName()).isEqualTo("Andrea");
assertThat(student.getParentAge()).isEqualTo(38);
assertThat(student.getSchoolName()).isEqualTo("Baeldung High School");
⚠️ 缺点是:构造函数参数越来越多,IDE 自动生成能帮上大忙。
3. 实验特性:Lombok @SuperBuilder
从 Lombok 1.18 开始,官方推出了一个更强大的实验性注解:@SuperBuilder
,专门用来优雅地解决继承下的 Builder 问题。
3.1. 应用注解
只需为所有相关类加上 @SuperBuilder
即可:
@Getter
@SuperBuilder
public class Parent {
private final String parentName;
private final int parentAge;
}
@Getter
@SuperBuilder
public class Child extends Parent {
private final String childName;
private final int childAge;
}
@Getter
@SuperBuilder
public class Student extends Child {
private final String schoolName;
}
⚠️ 注意事项:
- 所有类都必须使用
@SuperBuilder
,不能混用@Builder
- 否则编译器会直接报错 ❌
3.2. 使用 Builder
此时你不再需要手动编写构造函数,直接使用 Student.builder()
就可以访问所有层级的字段:
Student student = Student.builder()
.parentName("Andrea")
.parentAge(38)
.childName("Emma")
.childAge(6)
.schoolName("Baeldung High School")
.build();
assertThat(student.getChildName()).isEqualTo("Emma");
assertThat(student.getChildAge()).isEqualTo(6);
assertThat(student.getParentName()).isEqualTo("Andrea");
assertThat(student.getParentAge()).isEqualTo(38);
assertThat(student.getSchoolName()).isEqualTo("Baeldung High School");
✅ 优点显而易见:代码简洁、逻辑清晰、无需额外构造函数。
4. 总结
方式 | 是否推荐 | 说明 |
---|---|---|
手动构造 + @Builder |
✅ 推荐用于生产环境 | 兼容性好,适用于旧版本 Lombok |
@SuperBuilder |
⚠️ 实验特性,谨慎使用 | 更简洁,但要求整个继承链统一使用 |
无论哪种方式,只要理解其背后机制,都可以轻松应对继承结构中的 Builder 模式构建问题。
如需完整源码,请访问 GitHub 示例项目。