Lombok to comment form to simplify the java code, increase development efficiency. For example, we used the @Builder
, @Data
, @AllArgsConstructor
, @NoArgsConstructor
, @ToString
and so on.
However, recently discovered in the iteration Lombok (version: 1.16.20
or below this version) instantiate an object model builder default value in the new instance of reflection or incompatible. Here the basic data types is not the default value
Lombok by way of annotation, the compiler automatically generates attribute constructors, getter / setter, equals, hashcode, toString method. You can view generated by the bytecode decompilation. example:
1
2
3
4
5
6
7
8
9
10
|
@Builder
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public
class
A {
int
num;
Integer count;
Integer noticedCount =
0
;
}
|
Use the following
1
2
3
4
5
|
public
class
Test {
public
static
void
main(String[] args) {
A a = A.builder().count(
1
).noticedCount(
2
).build();
}
}
|
Write watched than the previous new A (), and then set the value much more convenient, of course, also be passed directly to the desired value in the constructor. But if the class attribute more, you will find the use of Lombok and the development efficiency is much higher.
Recently, however, when used in the project found a bug problem, used in the project in Lombok version 1.16.20. As in the example above, the A.builder().build()
instantiated found in a noticedCount
default value of null. The reason to view the generated class files, there is a A $ Builder.class, using javap -c A.class view bytecode class or directly drag the file to the idea, the generated code view, the idea is the following the code is shown in 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
|
package
com.test;
public
class
A$ABuilder {
private
int
num;
private
Integer count;
private
Integer noticedCount;
A$ABuilder() {
}
public
A$ABuilder num(
int
num) {
this
.num = num;
return
this
;
}
public
A$ABuilder count(Integer count) {
this
.count = count;
return
this
;
}
public
A$ABuilder noticedCount(Integer noticedCount) {
this
.noticedCount = noticedCount;
return
this
;
}
public
A build() {
return
new
A(
this
.num,
this
.count,
this
.noticedCount);
}
public
String toString() {
return
"A.ABuilder(num="
+
this
.num +
", count="
+
this
.count +
", noticedCount="
+
this
.noticedCount +
")"
;
}
}
|
Learn to see noticedCount
default values are not. Seen A.builder().build()
when the build () method of the object A is constructed using the attribute value of the internal class, so our example this initialization noticedCount
is empty.
After viewing the code in Lombok found a @Builder.Default
per comment, this is able to resolve initialization defaults. code show as below
1
2
3
4
5
6
7
8
9
10
11
|
@Builder
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public
class
A {
int
num;
Integer count;
@Builder
.Default
Integer noticedCount =
0
;
}
|
Look at the content generated A $ Builder.class file is as follows
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
|
package
com.test;
public
class
A$ABuilder {
private
int
num;
private
Integer count;
private
boolean
noticedCount$set;
private
Integer noticedCount;
A$ABuilder() {
}
public
A$ABuilder num(
int
num) {
this
.num = num;
return
this
;
}
public
A$ABuilder count(Integer count) {
this
.count = count;
return
this
;
}
public
A$ABuilder noticedCount(Integer noticedCount) {
this
.noticedCount = noticedCount;
this
.noticedCount$set =
true
;
return
this
;
}
public
A build() {
Integer noticedCount =
this
.noticedCount;
if
(!
this
.noticedCount$set) {
noticedCount = A.access$
000
();
}
return
new
A(
this
.num,
this
.count, noticedCount);
}
public
String toString() {
return
"A.ABuilder(num="
+
this
.num +
", count="
+
this
.count +
", noticedCount="
+
this
.noticedCount +
")"
;
}
}
|
可以看到代码中多了private boolean noticedCount$set
;这个就是确认是否需要设置默认值。
到这一步你以为就完美了吗??NO.
假如我们在Test方法中增加一行代码,如下,自己可以试试运行的结果看看输出的a与a1的结果
1
2
3
4
5
6
7
8
|
public
class
Test {
public
static
void
main(String[] args) {
A a = A.builder().count(
1
).noticedCount(
2
).build();
System.out.println(a);
A a1 =
new
A();
System.out.println(a1);
}
}
|
什么还需要new?有些场景中,比如其他第三方库使用这个类的时候,就不是通过builder模式来实例化对象,第三方库一般都是通过反射机制来实例化,然Lombok给我编译出来的class字节码已经不再是原有的。所以就出现问题了。
至于Lombok是如何实现的。可以研究下HandleBuilder
.里面有具体逻辑