【轉】Lombok Pojo默認初始值問題


Lombok以注解形式來簡化java代碼,提高開發效率。比如我們常用的@Builder@Data@AllArgsConstructor@NoArgsConstructor@ToString等。

然最近在迭代中發現Lombok(version:1.16.20或者低於這個版本)的builder模式與new實例化或者反射機制下實例化對象默認值不兼容。這里的默認值不是基本數據類型

Lombok是通過注解的方式,在編譯時自動為屬性生成構造器、getter/setter、equals、hashcode、toString方法。可以通過反編譯查看生成的字節碼。例子:

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 ;
}

使用方式如下

1
2
3
4
5
public  class  Test {
     public  static  void  main(String[] args) {
         A a = A.builder().count( 1 ).noticedCount( 2 ).build();
     }
}

  

這樣寫看着比以前的new A(),再set值方便多了,當然也可以在構造函數中直接傳入需要的值。但是如果類的屬性多了,就會發現Lombok使用以及開發效率上要高很多。

然而最近,在項目中使用的時候發現一個bug問題,項目中使用的Lombok的版本號1.16.20。如上面的例子,通過A.builder().build()實例化后,發現a中的noticedCount的默認值為null。究其原因,查看生成的class文件,有個A$Builder.class,使用javap -c A.class查看字節碼或者直接將這個class文件拖拽到idea中,查看生成的代碼,以下是在idea中展示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 +  ")" ;
     }
}

  

從中看到noticedCount默認值沒有。看出A.builder().build()中的build()方法構造A對象的時候是使用內部類的屬性值,所以這個初始化的實例我們的noticedCount值為空。

經過查看Lombok下的代碼發現有個@Builder.Default根據注釋,這個是能解決初始化默認值的。代碼如下

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 ;
} 

再看看生成的A$Builder.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
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.里面有具體邏輯


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM