前言
Lombok@Builder实现链式构建的弊病及替代方案。
@Builder的弊病
存在继承关系时十分复杂
错误用法:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Parent {
private final String parentName;
private final int parentAge;
}
public class Child extends Parent {
private final String childName;
private final int childAge;
}
// 报错:Implicit super constructor Parent() is undefined. Must explicitly invoke another constructor
正确用法: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
public class Parent {
private final String parentName;
private final int parentAge;
}
public class Child extends Parent {
private final String childName;
private final int childAge;
public Child(String parentName, int parentAge, String childName, int childAge) {
super(parentName, parentAge);
this.childName = childName;
this.childAge = childAge;
}
}
// 使用示例
Child child = Child.builder()
.parentName("Andrea")
.parentAge(38)
.childName("Emma")
.childAge(6)
.build();
但如果父类使用了@Builder注解呢?
1 |
|
此时用原子类会报错:Child中的builder()无法覆盖Parent中的builder()
。可以通过修改子类为如下形式解决:
1 |
|
但在链式构建子类对象时,就需要调用Child.childBuilder()
了。即便如此,在继承层级多时也十分繁琐。
在Lombok1.18中,提供了@SuperBuilder注解,可以解决上述问题。使用示例如下:1
2
3
4
5
6
7
8
9
10
11
12
13
public class Parent {
private final String parentName;
private final int parentAge;
}
public class Child extends Parent {
private final String childName;
private final int childAge;
}
不可与@Data同时使用
很多框架会使用类的无参构造函数创建对象,但是如果同时使⽤@Data和@Builder的话,会导致类的无参构造函数缺失。
并且,这种情况并不能通过手动添加无参构造函数或添加@NoArgsConstructor解决。
该问题只能通过引⼊注解@Tolerate来解决。
Lombok同时使⽤@Data和@Builder的时候,如果要⽣成⽆参构造,需要在代码⾥⾯⼿动引⼊注解@Tolerate,让Lombok在⽣成类的时候,对指定的构造函数不感知。
1 |
|
用@Accessors替代@Builder
在为了链式编程而使用@Builder时,它并非最佳实践。@Builder会额外创建内部类,无法与@Data兼容,且处理继承关系十分复杂。
使用@Accessors(chain = true)实现链式编程是更好的选择,简单示例如下: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
public class APIResponse<T> {
private T payload;
private Status status;
}
// 等价代码
public class APIResponse<T> {
private T payload;
private Status status;
public T getPayload() {
return this.payload;
}
public APIResponse<T> setPayload(T payload) {
this.payload = payload;
return this;
}
public Status getStatus() {
return this.status;
}
public APIResponse<T> setStatus(Status status) {
this.status = status;
return this;
}
}
// 使用示例
// 假设Status也使用了@Accessors(chain = true)
Status status = new Status().setResponseCode("RESPONSE_CODE_IDENTIFIER").setDescription("Bla Bla Bla");
return new APIResponse().setStatus(status);
实际运用时,如果属性较多,且分为必传属性和选填属性时,可以将必传参数定义在构造方法中,加上 @Accessors 注解,这样就可以实现必传参数的传入,又可以实现选填参数的链式调用。
假设 Student 类,它的 学生ID和年级和班级是必填的,姓名、性别、住址是选填的,那么示例代码如下:
1 |
|
参考
[1] https://www.baeldung.com/lombok-builder-inheritance
[2] https://mp.weixin.qq.com/s?__biz=MzIzOTU0NTQ0MA%3D%3D&mid=2247534735&idx=1&sn=c6363bff49edbe8b03b5789d2f5b18b9&chksm=e92a7580de5dfc967aaaa7d696a95a7361c4188fe8a626037f51cb70c53466f9ee78efef369d&scene=262&from=industrynews
后记
首发于 silencezheng.top,转载请注明出处。