Lombok 是一款非常实用的工具,通过注解能消除冗长的 Java 代码,工程中需要导入 lombok.jar,相应 IDE 的安装方式可参考 官网。项目中使用到的注解主要是 @Data 和 @Builder,前一个注解用在类上,相当于同时使用了 @ToString、@EqualsAndHashCode、@Getter、@Setter 和 @RequiredArgsConstrutor;后一个主要用在类、构造器、方法上,能非常方便的实现建造者模式、链式编程,相关文档可参考 这里。
@Builder 注解
在有继承(Inheritance)关系的实体类中使用 @Builder 时,遇到子类的 Builder 对象无法使用到父类的属性,以下方例子来说明
如果是使用 Parent p = new Parent(); p.setParentAge(123);...
方式赋值,那么以下内容可忽略!
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Data
public class Parent { private String parentName; private int parentAge; } @Data @Builder public class Child extends Parent { private String childName; private int childAge; }
|
在使用 Child.builder().parentAge(18).parentName("zhangsan")
时编译失败,查看 Child 对应的 class 文件如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| public class Child extends Parent { private String childName; private int childAge;
Child(String childName, int childAge) { this.childName = childName; this.childAge = childAge; }
public static Child.ChildBuilder builder() { return new Child.ChildBuilder(); }
public static class ChildBuilder { private String childName; private int childAge;
ChildBuilder() { }
public Child.ChildBuilder childName(String childName) { this.childName = childName; return this; }
public Child.ChildBuilder childAge(int childAge) { this.childAge = childAge; return this; }
public Child build() { return new Child(this.childName, this.childAge); }
public String toString() { return "Child.ChildBuilder(childName=" + this.childName + ", childAge=" + this.childAge + ")"; } } }
|
并未发现有父类的属性存在。若是在父类中加 @Builder 注解而子类不加,则会报如下错误
1 2 3 4
| Error:(14, 8) java: 无法将类 com.leaface.Parent中的构造器 Paren t应用到给定类型; 需要: java.lang.String,int 找到: 没有参数 原因: 实际参数列表和形式参数列表长度不同
|
之后参考了有关文档,将代码改成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @Data @NoArgsConstructor @AllArgsConstructor public class Parent { private final String parentName; private final int parentAge; } @Data 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; } }
|
PS:父类中的 @NoArgsConstructor 是在使用了 @Data 注解后,为解决子类出现 Lombok needs a default constructor in the base class
这个错误而加的
这样配置后,子类就能设置父类的属性了,注意:此时父类是没有加 @Builder 的,而我们项目中又需要这个注解,若是都加上 @Builder,子类编译报错
1 2
| Error:(17, 5) java: com.leaface.Child 中的 builder() 无法隐藏 com.leaface.Parent 中的 builder() 返回类型com.leaface.Child.ChildBuilder与com.leaface.Parent.ParentBuilder不兼容
|
以下是 @Builder 注解源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| @Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR}) @Retention(RetentionPolicy.SOURCE) public @interface Builder { String builderMethodName() default "builder";
String buildMethodName() default "build";
String builderClassName() default "";
boolean toBuilder() default false;
@Target({ElementType.FIELD}) @Retention(RetentionPolicy.SOURCE) public @interface Default { }
@Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.SOURCE) public @interface ObtainVia { String field() default "";
String method() default "";
boolean isStatic() default false; } }
|
其中的 builderMethodName 默认为 builder,子类试图公开和父类具有相同名称的构建器,所以报错。
解决方案:为父类和子类分别指定不同名称的构建器!!!
例如在子类中加 builderMethodName = "leafChildBuilder"
,修改后代码如下
1 2 3 4 5 6 7 8 9 10 11 12
| @Data public class Child extends Parent{ private String childName; private int childAge;
@Builder(builderMethodName = "leafChildBuilder") public Child(String parentName, int parentAge, String childName, int childAge) { super(parentName, parentAge); this.childName = childName; this.childAge = childAge; } }
|
查看子类的 class 文件,发现已生成其对应的构建器 leafChildBuilder() 和父类的属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| public static Child.ChildBuilder leafChildBuilder() { return new Child.ChildBuilder(); }
public Child(String parentName, int parentAge, String childName, int childAge) { super(parentName, parentAge); this.childName = childName; this.childAge = childAge; }
public static class ChildBuilder { private String parentName; private int parentAge; private String childName; private int childAge;
ChildBuilder() { }
public Child.ChildBuilder parentName(String parentName) { this.parentName = parentName; return this; }
public Child.ChildBuilder parentAge(int parentAge) { this.parentAge = parentAge; return this; } public Child.ChildBuilder childName(String childName) { this.childName = childName; return this; }
public Child.ChildBuilder childAge(int childAge) { this.childAge = childAge; return this; } public Child build() { return new Child(this.parentName, this.parentAge, this.childName, this.childAge); } }
|
这里没有改父类的构建器名称,所以还是默认的 builder(),两者已不存在冲突
1 2 3
| public static Parent.ParentBuilder builder() { return new Parent.ParentBuilder(); }
|
使用时,子类直接调 leafChildBuilder() 即可,父类还是使用默认的 builder()
1 2 3
| Child child = Child.leafChildBuilder().parentAge(88).childAge(18).childName("zhangsan").build();
Parent parent = Parent.builder().parentName("admin").parentAge(99).build();
|
@SuperBuilder 注解(推荐)
Lombok v1.18.2 推出了 @SuperBuilder 注解,这样就不用再定义特殊的构建器了,比如这样
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Data @NoArgsConstructor @SuperBuilder public class Parent { private String parentName; private int parentAge; } @Data @SuperBuilder public class Child extends Parent { private String childName; private int childAge; }
|
使用默认的 builder() 调用即可
1 2 3
| Child child = Child.builder().parentAge(88).childAge(18).childName("zhangsan").build();
Parent parent = Parent.builder().parentName("admin").parentAge(99).build();
|
以上两种方式,赋值后均能通过 @Data 提供的 get 方法取到值。
@SuperBuilder 和 @Builder 不能在同一继承体系中混用,否则会编译失败。多层级继承中这两者的使用方式同上!
至此,Lombok @Builder 继承问题解决!
附录
最后分别附上使用了 @Builder 和 @SuperBuilder 后的父类、子类 class 文件(省略了 @Data 注解)
@Builder class
Parent.class ======>>>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class Parent { private String parentName; private int parentAge;
public static Parent.ParentBuilder builder() { return new Parent.ParentBuilder();}
public Parent(String parentName, int parentAge) { this.parentName = parentName; this.parentAge = parentAge;}
public static class ParentBuilder { private String parentName; private int parentAge;
ParentBuilder() {}
public Parent.ParentBuilder parentName(String parentName) { this.parentName = parentName; return this; }
public Parent.ParentBuilder parentAge(int parentAge) { this.parentAge = parentAge; return this; }
public Parent build() { return new Parent(this.parentName, this.parentAge); }
public String toString() { return "Parent.ParentBuilder(parentName=" + this.parentName + ", parentAge=" + this.parentAge + ")"; } } }
|
Child.class ======>>>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| public class Child extends Parent { private String childName; private int childAge;
public Child(String parentName, int parentAge, String childName, int childAge) { super(parentName, parentAge); this.childName = childName; this.childAge = childAge; }
public static Child.ChildBuilder leafChildBuilder() { return new Child.ChildBuilder(); }
public static class ChildBuilder { private String parentName; private int parentAge; private String childName; private int childAge;
ChildBuilder() {}
public Child.ChildBuilder parentName(String parentName) { this.parentName = parentName; return this; }
public Child.ChildBuilder parentAge(int parentAge) { this.parentAge = parentAge; return this; }
public Child.ChildBuilder childName(String childName) { this.childName = childName; return this; }
public Child.ChildBuilder childAge(int childAge) { this.childAge = childAge; return this; }
public Child build() { return new Child(this.parentName, this.parentAge, this.childName, this.childAge); }
public String toString() { return "Child.ChildBuilder(parentName=" + this.parentName + ", parentAge=" + this.parentAge + ", childName=" + this.childName + ", childAge=" + this.childAge + ")"; } } }
|
@SuperBuilder class
Parent.class ======>>>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| public class Parent { private String parentName; private int parentAge;
protected Parent(Parent.ParentBuilder<?, ?> b) { this.parentName = b.parentName; this.parentAge = b.parentAge; }
public static Parent.ParentBuilder<?, ?> builder() { return new Parent.ParentBuilderImpl(); }
private static final class ParentBuilderImpl extends Parent.ParentBuilder<Parent, Parent.ParentBuilderImpl> {
private ParentBuilderImpl() {}
protected Parent.ParentBuilderImpl self() { return this; }
public Parent build() { return new Parent(this); } }
public abstract static class ParentBuilder<C extends Parent, B extends Parent.ParentBuilder<C, B>> { private String parentName; private int parentAge;
public ParentBuilder() {}
protected abstract B self();
public abstract C build();
public B parentName(String parentName) { this.parentName = parentName; return this.self(); }
public B parentAge(int parentAge) { this.parentAge = parentAge; return this.self(); }
public String toString() { return "Parent.ParentBuilder(parentName=" + this.parentName + ", parentAge=" + this.parentAge + ")"; } } }
|
Child.class ======>>>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| public class Child extends Parent { private String childName; private int childAge;
protected Child(Child.ChildBuilder<?, ?> b) { super(b); this.childName = b.childName; this.childAge = b.childAge; }
public static Child.ChildBuilder<?, ?> builder() { return new Child.ChildBuilderImpl(); }
private static final class ChildBuilderImpl extends Child.ChildBuilder<Child, Child.ChildBuilderImpl> {
private ChildBuilderImpl() {}
protected Child.ChildBuilderImpl self() { return this; }
public Child build() { return new Child(this); } }
public abstract static class ChildBuilder<C extends Child, B extends Child.ChildBuilder<C, B>> extends ParentBuilder<C, B> { private String childName; private int childAge;
public ChildBuilder() {}
protected abstract B self();
public abstract C build();
public B childName(String childName) { this.childName = childName; return this.self(); }
public B childAge(int childAge) { this.childAge = childAge; return this.self(); }
public String toString() { return "Child.ChildBuilder(super=" + super.toString() + ", childName=" + this.childName + ", childAge=" + this.childAge + ")"; } } }
|
参考资料
Lombok @Builder
Lombok @SuperBuilder
Project Lombok
Lombok @Builder with Inheritance