Builder 模式的链式调用写起来很方便,但是自己实现 Builder 模式要在 POJO 类中写较多代码。Lombok 的 @Builder注解可以方便的支持 Builder 模式,但是在继承场景下,会出现 Lombok @Builder注解不会为继承的字段生成代码的问题。
如下面代码所示:
@NoArgsConstructor@AllArgsConstructorpublic class Parent { private String b;}
@Builder@NoArgsConstructor@AllArgsConstructorpublic class Child extends Parent { private String b;}
在通过构造模式创建 Child 对象时,不能链式调用 a() 方法。
即使给父类Parent也添加@Builder注解,依然无法调用。
@Builder@NoArgsConstructor@AllArgsConstructorpublic class Parent { private String b;}
使用 Lombok @Builder注解 可以很方便的使用构造模式, 我们也可以自己实现 Builder 模式,这将有助于我们理解 Builder 模式在继承场景下问题的本质。
自己实现 Builder 模式主要有四个步骤:
public class Parent { private String b;
public static Parent.ParentBuilder builder() { return new Parent.ParentBuilder(); }
public Parent() { }
public Parent(String b) { this.b = b; }
public static class ParentBuilder { private String b;
ParentBuilder() { }
public Parent.ParentBuilder b(String b) { this.b = b; return this; }
public Parent build() { return new Parent(this.b); } }}
public class Child extends Parent { private String b;
public static Child.ChildBuilder builder() { return new Child.ChildBuilder(); }
public Child() { }
public Child(String b) { this.b = b; }
public static class ChildBuilder { private String b;
ChildBuilder() { }
public Child.ChildBuilder b(String b) { this.b = b; return this; }
public Child build() { return new Child(this.b); } }}
其实上面的代码就是 Lombok @Builder注解在背后为我们做的事情,也解释了为什么 Builder 模式在继承场景下会出现问题。类是继承的,但类中的 builder 类并无继承关系。
那么这个问题就无法解决了吗?如果没有办法解决,Builder 模式的威力将大打折扣。幸运的是,我从网上寻找到了一种解决方式。 原文:
Lombok’s @Builder annotation and inheritance I’ve written about Project Lombok’s @Builder annotation before (see here and here). We’ve started using it in our project some time ago in favour of the code generation library PojoBuilder. One thing has bugged me though during that time: Lombok’s @Builder annotation won’t generate code for inherited fields. It turns out, there is a solution to this problem. We have been using @Builder on the class itself, but you can also put it on a class’s constructor or on a static method. In that case, Lombok will create a setter method on the builder class for every parameter of the constructor/method. That means you can create a custom constructor with parameters for all the fields of the class including its superclass.
@AllArgsConstructorpublic class Parent { private String a;} public class extends Parent { private String b; @Builder private Child(String a, String b){ super (a); this.b = b; }}
By setting the visibility of the constructor to private you can make sure that it is only available to Lombok (thanks to Mathias for the tip!). As a result you can then use the generated builder like this:
Child.builder().a("testA").b("testB").build()
The official documentation explains this, but it doesn’t explicitly point out that you can facilitate it in this way.
我尝试着做了翻译:
我曾经写过有关 Lombok _@Builder _注解的文章。不久前,我们开始在项目中使用到了它。但在此期间,有一件事情困扰着我: Lombok _@Builder _注解不会为继承的字段生成代码。事实证明,这个问题有一个解决方案。
我们通常都是将 _@Builder _注解用于类本身,但是同样可以将其用于类的构造方法或者是静态方法上。如果是这样, Lombok 会在 builder 类中为构造方法或者静态方法的每一个参数创建 setter 方法。这意味着,你可以创建一个自定义的构造方法,其中包含该类(包括其超类)所有字段的参数。
@AllArgsConstructorpublic class Parent { private String a;}
public class Child extends Parent { private String b;
@Builder private Child(String a, String b){ super(a); this.b = b; }}
通过将构造方法的可见性设置成私有的,可以确保其只对 Lombok 可用。因此你可以使用生成的构建器,如下:
Child.builder().a("testA").b("testB").build()
官方文档对其进行了解释,但是并没有明确指出可以通过这种方式提供便利。
Lombok’s @Builder annotation and inheritance