Lombok @Builder注解的兩大陷阱分析及解決
問題概述
Lombok 的 @Builder 注解極大地簡化了“建造者模式”的代碼編寫,但其默認(rèn)行為存在兩個(gè)非常隱蔽的陷阱:
陷阱一:無參構(gòu)造函數(shù)消失
現(xiàn)象:當(dāng)一個(gè)類同時(shí)使用 @Data 和 @Builder 后,原本可用的無參構(gòu)造函數(shù) new Entity() 突然不可用,編譯報(bào)錯(cuò)。
陷阱二:字段默認(rèn)值(初始化)失效
現(xiàn)象:在字段聲明時(shí)設(shè)置的默認(rèn)值(如 String status = “open”;),通過 Builder 創(chuàng)建對(duì)象時(shí),若未顯式設(shè)置該字段,其值將為 null 而非預(yù)期的 “open”。
陷阱一:無參構(gòu)造函數(shù)消失
復(fù)現(xiàn)步驟
編寫一個(gè)同時(shí)使用 @Data 和 @Builder 的類。
@Data
@Builder
public class User {
private String name;
private Integer age;
}
嘗試在其他地方使用無參構(gòu)造
User user = new User(); // 編譯錯(cuò)誤:找不到符號(hào) User()
原因分析
- @Data 會(huì)生成一個(gè) @RequiredArgsConstructor。如果類中沒有 final 或@NonNull 的字段,這個(gè)構(gòu)造器就是無參的。此時(shí),單獨(dú)使用 @Data 是完全可以無參構(gòu)造的。
- @Builder 注解需要基于一個(gè)全參構(gòu)造函數(shù)來工作。當(dāng)它發(fā)現(xiàn)你沒有顯式提供任何構(gòu)造器時(shí),它會(huì)主動(dòng)生成一個(gè)私有的、包含所有字段的全參構(gòu)造函數(shù)。
- @Builder 的生成行為取代了 @Data 生成 @RequiredArgsConstructor 的邏輯。最終,編譯后的類中只剩下 @Builder 生成的私有全參構(gòu)造器,導(dǎo)致無參構(gòu)造器確實(shí)“消失”了。
解決方案
顯式聲明所需的全部構(gòu)造器,避免 Lombok 的默認(rèn)行為產(chǎn)生沖突。
@Data
@Builder
@NoArgsConstructor // 顯式指定:我需要無參構(gòu)造
@AllArgsConstructor // 顯式指定:我需要全參構(gòu)造(讓Builder直接使用這個(gè)現(xiàn)成的)
public class User {
private String name;
private Integer age;
}
陷阱二:字段默認(rèn)值(初始化)失效
復(fù)現(xiàn)步驟
編寫一個(gè)帶有字段默認(rèn)值的類并使用 Builder。
@Data
@Builder
public class Order {
private Long id;
private String status = "open"; // 期望的默認(rèn)值
}
使用 Builder 創(chuàng)建對(duì)象,但不設(shè)置 status 字段。
Order order = Order.builder().id(1L).build(); System.out.println(order.getStatus()); // 輸出:null (預(yù)期是 "open")
原因分析
- 字段的默認(rèn)值 = "open"是在類的構(gòu)造函數(shù)內(nèi)部執(zhí)行的。
- @Builder 的 build() 方法本質(zhì)上是調(diào)用了類的全參構(gòu)造函數(shù),并將 Builder 對(duì)象內(nèi)部的字段值作為參數(shù)傳入。關(guān)鍵點(diǎn)在于:構(gòu)造函數(shù)會(huì)無條件使用傳入的參數(shù)。
- 如果你沒有調(diào)用 .status(…) 方法,Builder 對(duì)象內(nèi)部的 status 字段值就是 null。在調(diào)用 build() 時(shí),執(zhí)行的是 new Order(id, status),這個(gè) null 被傳入了構(gòu)造函數(shù),完全覆蓋了類定義中 = “open” 的初始化操作。
解決方案
使用 Lombok 提供的 @Builder.Default 注解。
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Order {
private Long id;
@Builder.Default // 使用此注解標(biāo)記
private String status = "open";
}
@Builder.Default 的原理:
- 它會(huì)指示 Lombok 在生成的 Builder 類中,預(yù)先將這個(gè)字段初始化為指定的默認(rèn)值。
- 如果后續(xù)沒有顯式設(shè)置該字段,build() 方法就會(huì)使用這個(gè)預(yù)先存在的默認(rèn)值,而不是 null。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
基于Java實(shí)現(xiàn)回調(diào)監(jiān)聽工具類
這篇文章主要為大家詳細(xì)介紹了如何基于Java實(shí)現(xiàn)一個(gè)回調(diào)監(jiān)聽工具類,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-04-04
java針對(duì)于時(shí)間轉(zhuǎn)換的DateUtils工具類
這篇文章主要為大家詳細(xì)介紹了java針對(duì)于時(shí)間轉(zhuǎn)換的DateUtils工具類,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12
springBoot啟動(dòng)時(shí)讓方法自動(dòng)執(zhí)行的幾種實(shí)現(xiàn)方式
這篇文章主要介紹了springBoot啟動(dòng)時(shí)讓方法自動(dòng)執(zhí)行的幾種實(shí)現(xiàn)方式,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
SpringBoot生產(chǎn)環(huán)境打包如何去除無用依賴
這篇文章主要介紹了SpringBoot生產(chǎn)環(huán)境打包如何去除無用依賴問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09

