[Turn] Lombok Pojo default initial value problem

Lombok to comment form to simplify the java code, increase development efficiency. For example, we used the @Builder, @Data, @AllArgsConstructor, @NoArgsConstructor, @ToStringand so on.

However, recently discovered in the iteration Lombok (version: 1.16.20or 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 noticedCountdefault 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 noticedCountdefault 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 noticedCountis empty.

After viewing the code in Lombok found a @Builder.Defaultper 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应该也发现了,在1.18.2以上fix这个bug了。大家可以试试。所以建议大家升级下版本

至于Lombok是如何实现的。可以研究下HandleBuilder.里面有具体逻辑

Guess you like

Origin www.cnblogs.com/exmyth/p/11243888.html