@Builder和@SuperBuilder的使用和原理

876 阅读4分钟

@Builder的作用和原理

@Builder是Lombok提供的一个注解,基于构建者模式原理实现的,日常主要用来简化代码,通过链式调用来构建实例对象;

比如有这样一个基础数据类:

import java.util.Date;  
  
public class CommonModel {  
    private int sex;  
    private int age;  
    private String name;  
    private float height;  
    private float weight;  
    private Date birthDay;
}

Lombok会在编译后的静态字节码文件中创建一个 名为CommonModelBuilder的静态内部构建类和该类的一个全参数构造器,在这个构建类中,为每个字段都创建了对应的方法,方法的返回值都是构建类自身,从而实现了链式调用;

默认只会生成一个全参构造器

import java.util.Date;  
  
public class CommonModel {  
    private int sex;  
    private int age;  
    private String name;  
    private float height;  
    private float weight;  
    private Date birthDay;  
  
    CommonModel(final int sex, final int age, final String name, final float height, final float weight, final Date birthDay) {  
        this.sex = sex;  
        this.age = age;  
        this.name = name;  
        this.height = height;  
        this.weight = weight;  
        this.birthDay = birthDay;  
    }  
  
    public static CommonModelBuilder builder() {  
        return new CommonModelBuilder();  
    }  
  
    public static class CommonModelBuilder {  
        private int sex;  
        private int age;  
        private String name;  
        private float height;  
        private float weight;  
        private Date birthDay;  
  
        CommonModelBuilder() {  
        }  
  
        public CommonModelBuilder sex(final int sex) {  
            this.sex = sex;  
            return this;  
        }  
  
        public CommonModelBuilder age(final int age) {  
            this.age = age;  
            return this;  
        }  
  
        public CommonModelBuilder name(final String name) {  
            this.name = name;  
            return this;  
        }  
  
        public CommonModelBuilder height(final float height) {  
            this.height = height;  
            return this;  
        }  
  
        public CommonModelBuilder weight(final float weight) {  
            this.weight = weight;  
            return this;  
        }  
  
        public CommonModelBuilder birthDay(final Date birthDay) {  
            this.birthDay = birthDay;  
            return this;  
        }  
  
        public CommonModel build() {  
            return new CommonModel(this.sex, this.age, this.name, this.height, this.weight, this.birthDay);  
        }  
  
        public String toString() {  
            return "CommonModel.CommonModelBuilder(sex=" + this.sex + ", age=" + this.age + ", name=" + this.name + ", height=" + this.height + ", weight=" + this.weight + ", birthDay=" + this.birthDay + ")";  
        }  
    }  
}

@SuperBuilder的使用

在涉及到继承相关的子父类字段时,要使用@SuperBuilder

比如,在这样的一个场景下:

import lombok.Builder;  
  
@Builder  
public class Person{  
    private String name;  
}
import lombok.Builder;  
  
@Builder  
public class Man extends Person{  
    private String career;  
}

编译报错:

无法将Person中的构造器 Person应用到给定类型;
  需要: java.lang.String
  找到: 没有参数
  原因: 实际参数列表和形式参数列表长度不同

Man构建的时候会去构建子类Person,但是Person由于加了@Builder,没有无参构建函数;

public class Man extends Person {  
    private String career;  
  
    Man(final String career) {  
        this.career = career;  
    }  
  
    public static ManBuilder builder() {  
        return new ManBuilder();  
    }  
  
    public static class ManBuilder {  
        private String career;  
  
        ManBuilder() {  
        }  
  
        public ManBuilder career(final String career) {  
            this.career = career;  
            return this;  
        }  
  
        public Man build() {  
            return new Man(this.career);  
        }  
  
        public String toString() {  
            return "Man.ManBuilder(career=" + this.career + ")";  
        }  
    }  
}

既然是因为没有无参构造函数,那么就在子类上添加无参构造函数@NoArgsConstructor

但是idea提示错误,Lombok @Builder needs a proper constructor for this class,那么再加上@AllArgsConstructor,成功消除报错,但是编译报错,提示:

java: Man中的builder()无法覆盖Person中的builder()
返回类型Man.ManBuilder与Person.PersonBuilder不兼容

  该错误是因为Man类中的builder()方法试图重写Person类中的builder()方法,但是返回类型不匹配导致的。Person类的builder()方法返回的是PersonBuilder类型的对象。Man类继承自Person,但其builder()方法返回的是ManBuilder类型的对象。

Java的编译规则:

  • 在Java中,子类的方法如果要重写父类的方法,那么子类方法的返回类型必须是父类方法返回类型的子类型或者相同类型。

这里ManBuilder和PersonBuilder是两个独立的类型,不是继承关系,因此不符合Java的重写规则。

这时候,@SuperBuilder就派上用场了

@SuperBuilder 会自动生成一个构建器类(例如 ManBuilder),并且这个构建器类会继承自父类的构建器类(如果有)。这样可以确保构建器之间的类型兼容性。

例如:如果 Person 类使用了 @SuperBuilder,那么 Man 类继承 Person 并使用 @SuperBuilder 时,Lombok 会确保 ManBuilder 是 PersonBuilder 的子类。

import lombok.experimental.SuperBuilder;  
  
@SuperBuilder  
public class Person{  
    private String name;  
}
import lombok.experimental.SuperBuilder;  
  
@SuperBuilder  
public class Man extends Person{  
    private String career;  
}

查看编译后的字节码文件:

ManBuilder<C extends Man, B extends ManBuilder<C, B>> extends Person.PersonBuilder<C, B>

确实ManBuilder继承了子类的构建类PersonBuilder,解决了类型兼容问题;   而且可以看到,@SpringBuilder生成了两个类,一个内部静态抽象构建类,一个内部静态实现构建类

public class Person {  
    private String name;  
  
    protected Person(final PersonBuilder<?, ?> b) {  
        this.name = b.name;  
    }  
  
    public static PersonBuilder<?, ?> builder() {  
        return new PersonBuilderImpl();  
    }  
  
    private static final class PersonBuilderImpl extends PersonBuilder<Person, PersonBuilderImpl> {  
        private PersonBuilderImpl() {  
        }  
  
        protected PersonBuilderImpl self() {  
            return this;  
        }  
  
        public Person build() {  
            return new Person(this);  
        }  
    }  
  
    public abstract static class PersonBuilder<C extends Person, B extends PersonBuilder<C, B>> {  
        private String name;  
  
        public PersonBuilder() {  
        }  
  
        protected abstract B self();  
  
        public abstract C build();  
  
        public B name(final String name) {  
            this.name = name;  
            return this.self();  
        }  
  
        public String toString() {  
            return "Person.PersonBuilder(name=" + this.name + ")";  
        }  
    }  
}
public class Man extends Person {  
    private String career;  
  
    protected Man(final ManBuilder<?, ?> b) {  
        super(b);  
        this.career = b.career;  
    }  
  
    public static ManBuilder<?, ?> builder() {  
        return new ManBuilderImpl();  
    }  
  
    private static final class ManBuilderImpl extends ManBuilder<Man, ManBuilderImpl> {  
        private ManBuilderImpl() {  
        }  
  
        protected ManBuilderImpl self() {  
            return this;  
        }  
  
        public Man build() {  
            return new Man(this);  
        }  
    }  
  
    public abstract static class ManBuilder<C extends Man, B extends ManBuilder<C, B>> extends Person.PersonBuilder<C, B> {  
        private String career;  
  
        public ManBuilder() {  
        }  
  
        protected abstract B self();  
  
        public abstract C build();  
  
        public B career(final String career) {  
            this.career = career;  
            return this.self();  
        }  
  
        public String toString() {  
            return "Man.ManBuilder(super=" + super.toString() + ", career=" + this.career + ")";  
        }  
    }  
}