Java中Lombok的@Builder注解注意事項(xiàng)
前言
現(xiàn)在很多程序員都習(xí)慣使用Lombok來使代碼更加 “簡潔”。
但是使用Lombok也會(huì)造成很多問題,尤其@Builder 有個(gè)很大的坑,已經(jīng)見過好幾次由于使用@Builder注解導(dǎo)致默認(rèn)值失效的問題,如果測試時(shí)沒有在意這個(gè)問題,就很容易引發(fā)線上問題。
問題復(fù)現(xiàn)
我們隨便定義一個(gè)類Config,對其中兩個(gè)屬性設(shè)置默認(rèn)值。
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í)例時(shí),僅設(shè)置name屬性,然后打印出實(shí)例
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è)置的默認(rèn)值失效了。
原因分析
想了解為什么會(huì)這樣,我們只需要查看使用Lombok的注解后的Config類的 class文件長啥樣就明白了。
@Builder通過Lombok的注解處理器,在編譯時(shí)會(huì)自動(dòng)生成一個(gè)靜態(tài)內(nèi)部類,這個(gè)內(nèi)部類就是所謂的builder類,它包含了和被注解的類中的屬性一一對應(yīng)的setter方法,并且在build()方法中返回一個(gè)被注解的類的對象。
這個(gè)builder類的代碼實(shí)現(xiàn)是通過Lombok生成的,所以我們不需要手動(dòng)編寫。
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è)置的默認(rèn)值。
調(diào)用build方法時(shí), ConfigBuilder會(huì)調(diào)用全參的構(gòu)造方法來構(gòu)造Config 對象。
解決方法
使用@Builder.Default注解來標(biāo)識(shí)帶默認(rèn)值的屬性
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 + ")"; } } }
每個(gè)設(shè)置默認(rèn)值的屬性都會(huì)在Builder中加上是否設(shè)置的標(biāo)記,如果沒有主動(dòng)設(shè)置值,則調(diào)用Config中的默認(rèn)值的靜態(tài)方法進(jìn)行賦值,然后再調(diào)用Config全參構(gòu)造方法構(gòu)造該對象。
使用@Builder注解的缺點(diǎn)
- 如果在類上使用了@Builder 注解,那么你需要手動(dòng)添加一個(gè)無參構(gòu)造函數(shù),否則有些序列化框架需要通過newInstance構(gòu)造對象時(shí)會(huì)報(bào)錯(cuò)。
- 如果在類上使用了@Builder注解,就不能再在構(gòu)造函數(shù)或方法上使用 @Builder注解,否則會(huì)導(dǎo)致重復(fù)生成構(gòu)造器類
- 如果在類上使用了@Builder 注解,想給某個(gè)屬性設(shè)置一個(gè)默認(rèn)值,還需要在屬性上使用@Builder.Default 注解,否則默認(rèn)值會(huì)被忽略。
- 如果想讓子類繼承父類的屬性,那么你需要在子類的全參構(gòu)造函數(shù)上使用 @Builder注解,并且在父類上使用@AllArgsConstructor注解,否則子類的構(gòu)造器類不會(huì)包含父類的屬性
到此這篇關(guān)于Java中Lombok的@Builder注解注意事項(xiàng)的文章就介紹到這了,更多相關(guān)Lombok的@Builder注解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java編程實(shí)現(xiàn)計(jì)算兩個(gè)日期的月份差實(shí)例代碼
這篇文章主要介紹了Java編程實(shí)現(xiàn)計(jì)算兩個(gè)日期的月份差實(shí)例代碼,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-01-01SpringCloud?hystrix斷路器與全局解耦全面介紹
什么是服務(wù)降級(jí)?當(dāng)服務(wù)器壓力劇增的情況下,根據(jù)實(shí)際業(yè)務(wù)情況及流量,對一些服務(wù)和頁面有策略的不處理或換種簡單的方式處理,從而釋放服務(wù)器資源以保證核心交易正常運(yùn)作或高效運(yùn)作2022-10-10Dwr3.0純注解(純Java Code配置)配置與應(yīng)用淺析三之后端反向調(diào)用前端
Dwr是為人所熟知的前端框架,其異步推送功能是為人所津津樂道的,下來主要研究一下它的這個(gè)功能是怎么應(yīng)用的;2016-04-04詳解Springboot應(yīng)用中設(shè)置Cookie的SameSite屬性
Chrome 51 開始,瀏覽器的 Cookie 新增加了一個(gè)SameSite屬性,用來防止 CSRF 攻擊和用戶追蹤。今天通過本文給大家介紹Springboot應(yīng)用中設(shè)置Cookie的SameSite屬性,感興趣的朋友一起看看吧2022-01-01基于SpringBoot實(shí)現(xiàn)自動(dòng)裝配返回屬性的設(shè)計(jì)思路
這篇文章主要介紹了基于SpringBoot實(shí)現(xiàn)自動(dòng)裝配返回屬性,這里涉及到的技術(shù)知識(shí)點(diǎn)有注解解析器,為什么用ResponseBodyAdvice這里解析?不在Filter,Interceptors,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧2022-03-03IDEA?一直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í)行這些查詢的運(yùn)行時(shí),下文更多相關(guān)內(nèi)容介紹需要的小伙伴可以參考一下2022-04-04Spring需要三個(gè)級(jí)別緩存解決循環(huán)依賴原理解析
這篇文章主要為大家介紹了Spring需要三個(gè)級(jí)別緩存解決循環(huán)依賴原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02