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() → 调用父类的 builder
  • Child.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 示例项目