LombokPojo默认初始值问题
Lombok以注解形式来简化java代码,提⾼开发效率。⽐如我们常⽤
的@Builder、@Data、@AllArgsConstructor、@NoArgsConstructor、@ToString等。
然最近在迭代中发现Lombok(version:1.16.20或者低于这个版本)的builder模式与new实例化或者反射机制下实例化对象默认值不兼容。这⾥的默认值不是基本数据类型
Lombok是通过注解的⽅式,在编译时⾃动为属性⽣成构造器、getter/tter、equals、hashcode、toString⽅法。可以通过反编译查看⽣成的字节码。例⼦:
@Builder
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class A {
int num;
Integer count;
Integer noticedCount = 0;
}
使⽤⽅式如下
public class Test {
public static void main(String[] args) {
A a = A.builder().count(1).noticedCount(2).build();
}
}
这样写看着⽐以前的new A(),再t值⽅便多了,当然也可以在构造函数中直接传⼊需要的值。但是如果类的属性多了,就会发现Lombok 使⽤以及开发效率上要⾼很多。
然⽽最近,在项⽬中使⽤的时候发现⼀个bug问题,项⽬中使⽤的Lombok的版本号1.16.20。如上⾯的例⼦,通过A.builder().build()实例化后,发现a中的noticedCount的默认值为null。究其原因,查看⽣成的class⽂件,有个A$Builder.class,使⽤javap -c A.class查看字节码或者直接将这个class⽂件拖拽到idea中,查看⽣成的代码,以下是在idea中展⽰class的代码
st;
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) {
return this;
}
public A$ABuilder noticedCount(Integer noticedCount) {
return this;
}
public A build() {
return new A(this.num, unt, icedCount);
}
public String toString() {
return "A.ABuilder(num=" + this.num + ", count=" + unt + ", noticedCount=" + icedCount + ")";
}
}
从中看到noticedCount默认值没有。看出A.builder().build()中的build()⽅法构造A对象的时候是使⽤内部类的属性值,所以这个初始化的实例我们的noticedCount值为空。
经过查看Lombok下的代码发现有个@Builder.Default根据注释,这个是能解决初始化默认值的。代码
如下
@Builder
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class A {
int num;
Integer count;
@Builder.Default
Integer noticedCount = 0;
}
再看看⽣成的A$Builder.class⽂件的内容如下
st;
public class A$ABuilder {
private int num;
private Integer count;
private boolean noticedCount$t;
private Integer noticedCount;
A$ABuilder() {
}
public A$ABuilder num(int num) {
this.num = num;
return this;
}
public A$ABuilder count(Integer count) {
return this;
}
public A$ABuilder noticedCount(Integer noticedCount) {
return this;
}
public A build() {
Integer noticedCount = icedCount;
if (!icedCount$t) {
noticedCount = A.access$000();
}
return new A(this.num, unt, noticedCount);
}
public String toString() {
return "A.ABuilder(num=" + this.num + ", count=" + unt + ", noticedCount=" + icedCount + ")";
}
}
可以看到代码中多了private boolean noticedCount$t;这个就是确认是否需要设置默认值。
到这⼀步你以为就完美了吗??NO.
假如我们在Test⽅法中增加⼀⾏代码,如下,⾃⼰可以试试运⾏的结果看看输出的a与a1的结果
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应该也发现了,在1.18.2以上fix这个bug了。⼤家可以试试。所以建议⼤家升级下版本
⾄于Lombok是如何实现的。可以研究下HandleBuilder.⾥⾯有具体逻辑