Java中Lombok的@Builder注解注意事項
前言
現(xiàn)在很多程序員都習(xí)慣使用Lombok來使代碼更加 “簡潔”。
但是使用Lombok也會造成很多問題,尤其@Builder 有個很大的坑,已經(jīng)見過好幾次由于使用@Builder注解導(dǎo)致默認值失效的問題,如果測試時沒有在意這個問題,就很容易引發(fā)線上問題。
問題復(fù)現(xiàn)
我們隨便定義一個類Config,對其中兩個屬性設(shè)置默認值。
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class Config {
private boolean isOpen = true;
private String name;
private int value = 20;
}
public class LombokDemo {
public static void main(String[] args) {
Config config = Config.builder().name("test").build();
System.out.println(config);
}
}借助Builder模式創(chuàng)建Config類實例時,僅設(shè)置name屬性,然后打印出實例
public class LombokDemo {
public static void main(String[] args) {
Config config = Config.builder().name("test").build();
System.out.println(config);
}
}輸出結(jié)果如下。
Config(isOpen=false, name=test, value=0)
我們?yōu)閕sOpen及value屬性設(shè)置的默認值失效了。
原因分析
想了解為什么會這樣,我們只需要查看使用Lombok的注解后的Config類的 class文件長啥樣就明白了。
@Builder通過Lombok的注解處理器,在編譯時會自動生成一個靜態(tài)內(nèi)部類,這個內(nèi)部類就是所謂的builder類,它包含了和被注解的類中的屬性一一對應(yīng)的setter方法,并且在build()方法中返回一個被注解的類的對象。
這個builder類的代碼實現(xiàn)是通過Lombok生成的,所以我們不需要手動編寫。
public class Config {
private boolean isOpen = true;
private String name;
private int value = 20;
Config(boolean isOpen, String name, int value) {
this.isOpen = isOpen;
this.name = name;
this.value = value;
}
public static ConfigBuilder builder() {
return new ConfigBuilder();
}
public boolean isOpen() {
return this.isOpen;
}
public String getName() {
return this.name;
}
public int getValue() {
return this.value;
}
public void setOpen(boolean isOpen) {
this.isOpen = isOpen;
}
public void setName(String name) {
this.name = name;
}
public void setValue(int value) {
this.value = value;
}
public boolean equals(Object o) {
// 省略
}
protected boolean canEqual(Object other) {
return other instanceof Config;
}
public int hashCode() {
// 省略
}
public String toString() {
return "Config(isOpen=" + this.isOpen() + ", name=" + this.getName() + ", value=" + this.getValue() + ")";
}
public static class ConfigBuilder {
private boolean isOpen;
private String name;
private int value;
ConfigBuilder() {
}
public ConfigBuilder isOpen(boolean isOpen) {
this.isOpen = isOpen;
return this;
}
public ConfigBuilder name(String name) {
this.name = name;
return this;
}
public ConfigBuilder value(int value) {
this.value = value;
return this;
}
public Config build() {
return new Config(this.isOpen, this.name, this.value);
}
public String toString() {
return "Config.ConfigBuilder(isOpen=" + this.isOpen + ", name=" + this.name + ", value=" + this.value + ")";
}
}
}可以看到,ConfigBuilder中isOpen和value屬性并沒有使用我們想要設(shè)置的默認值。
調(diào)用build方法時, ConfigBuilder會調(diào)用全參的構(gòu)造方法來構(gòu)造Config 對象。
解決方法
使用@Builder.Default注解來標識帶默認值的屬性
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class SomeConfig {
@Builder.Default
private boolean isOpen = true;
private String name;
@Builder.Default
private int value = 20;
}修改后輸出結(jié)果如下
Config(isOpen=true, name=test, value=20)
為什么加了@Builder.Default注解就能解決問題呢,看一下編譯后的class文件就明白了
public class Config {
private boolean isOpen;
private String name;
private int value;
private static boolean $default$isOpen() {
return true;
}
private static int $default$value() {
return 20;
}
Config(boolean isOpen, String name, int value) {
this.isOpen = isOpen;
this.name = name;
this.value = value;
}
public static ConfigBuilder builder() {
return new ConfigBuilder();
}
public boolean isOpen() {
return this.isOpen;
}
public String getName() {
return this.name;
}
public int getValue() {
return this.value;
}
public void setOpen(boolean isOpen) {
this.isOpen = isOpen;
}
public void setName(String name) {
this.name = name;
}
public void setValue(int value) {
this.value = value;
}
public boolean equals(Object o) {
// 省略
}
protected boolean canEqual(Object other) {
return other instanceof Config;
}
public int hashCode() {
// 省略
}
public String toString() {
boolean var10000 = this.isOpen();
return "Config(isOpen=" + var10000 + ", name=" + this.getName() + ", value=" + this.getValue() + ")";
}
public static class ConfigBuilder {
private boolean isOpen$set;
private boolean isOpen$value;
private String name;
private boolean value$set;
private int value$value;
ConfigBuilder() {
}
public ConfigBuilder isOpen(boolean isOpen) {
this.isOpen$value = isOpen;
this.isOpen$set = true;
return this;
}
public ConfigBuilder name(String name) {
this.name = name;
return this;
}
public ConfigBuilder value(int value) {
this.value$value = value;
this.value$set = true;
return this;
}
public Config build() {
boolean isOpen$value = this.isOpen$value;
if (!this.isOpen$set) {
isOpen$value = Config.$default$isOpen();
}
int value$value = this.value$value;
if (!this.value$set) {
value$value = Config.$default$value();
}
return new Config(isOpen$value, this.name, value$value);
}
public String toString() {
return "Config.ConfigBuilder(isOpen$value=" + this.isOpen$value + ", name=" + this.name + ", value$value=" + this.value$value + ")";
}
}
}每個設(shè)置默認值的屬性都會在Builder中加上是否設(shè)置的標記,如果沒有主動設(shè)置值,則調(diào)用Config中的默認值的靜態(tài)方法進行賦值,然后再調(diào)用Config全參構(gòu)造方法構(gòu)造該對象。
使用@Builder注解的缺點
- 如果在類上使用了@Builder 注解,那么你需要手動添加一個無參構(gòu)造函數(shù),否則有些序列化框架需要通過newInstance構(gòu)造對象時會報錯。
- 如果在類上使用了@Builder注解,就不能再在構(gòu)造函數(shù)或方法上使用 @Builder注解,否則會導(dǎo)致重復(fù)生成構(gòu)造器類
- 如果在類上使用了@Builder 注解,想給某個屬性設(shè)置一個默認值,還需要在屬性上使用@Builder.Default 注解,否則默認值會被忽略。
- 如果想讓子類繼承父類的屬性,那么你需要在子類的全參構(gòu)造函數(shù)上使用 @Builder注解,并且在父類上使用@AllArgsConstructor注解,否則子類的構(gòu)造器類不會包含父類的屬性
到此這篇關(guān)于Java中Lombok的@Builder注解注意事項的文章就介紹到這了,更多相關(guān)Lombok的@Builder注解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringCloud?hystrix斷路器與全局解耦全面介紹
什么是服務(wù)降級?當(dāng)服務(wù)器壓力劇增的情況下,根據(jù)實際業(yè)務(wù)情況及流量,對一些服務(wù)和頁面有策略的不處理或換種簡單的方式處理,從而釋放服務(wù)器資源以保證核心交易正常運作或高效運作2022-10-10
Dwr3.0純注解(純Java Code配置)配置與應(yīng)用淺析三之后端反向調(diào)用前端
Dwr是為人所熟知的前端框架,其異步推送功能是為人所津津樂道的,下來主要研究一下它的這個功能是怎么應(yīng)用的;2016-04-04
詳解Springboot應(yīng)用中設(shè)置Cookie的SameSite屬性
Chrome 51 開始,瀏覽器的 Cookie 新增加了一個SameSite屬性,用來防止 CSRF 攻擊和用戶追蹤。今天通過本文給大家介紹Springboot應(yīng)用中設(shè)置Cookie的SameSite屬性,感興趣的朋友一起看看吧2022-01-01
基于SpringBoot實現(xiàn)自動裝配返回屬性的設(shè)計思路
這篇文章主要介紹了基于SpringBoot實現(xiàn)自動裝配返回屬性,這里涉及到的技術(shù)知識點有注解解析器,為什么用ResponseBodyAdvice這里解析?不在Filter,Interceptors,本文結(jié)合示例代碼給大家介紹的非常詳細,需要的朋友參考下吧2022-03-03
IDEA?一直scanning?files?to?index的四種完美解決方法(VIP典藏版)
這篇文章主要介紹了IDEA?一直scanning?files?to?index的四種完美解決方法(VIP典藏版),推薦第四種方法,第四種方法摸索研究后得出,親測好用,需要的朋友參考下吧2023-10-10
利用Spring?Boot和JPA創(chuàng)建GraphQL?API
這篇文章主要介紹了利用Spring?Boot和JPA創(chuàng)建GraphQL?API,GraphQL既是API查詢語言,也是使用當(dāng)前數(shù)據(jù)執(zhí)行這些查詢的運行時,下文更多相關(guān)內(nèi)容介紹需要的小伙伴可以參考一下2022-04-04
Spring需要三個級別緩存解決循環(huán)依賴原理解析
這篇文章主要為大家介紹了Spring需要三個級別緩存解決循環(huán)依賴原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-02-02

