Lombok 子類如何使用 @Builder
點擊上方?好好學java?,選擇?星標?公眾號
重磅資訊、干貨,第一時間送達 今日推薦:分享一套基于SpringBoot和Vue的企業(yè)級中后臺開源項目,這個項目有點哇塞!
個人原創(chuàng)100W+訪問量博客:點擊前往,查看更多
作者:jitwxs
https://jitwxs.cn/54621f54.html
一、前言
業(yè)務(wù)開發(fā)中,子類父類還算是經(jīng)常用到,Lombok 的 @builder 提供的鏈式調(diào)用幫助我們更輕松的創(chuàng)建對象。但是實驗后卻發(fā)現(xiàn)子類的 @Builder 是不會包含父類的屬性。
假設(shè)存在父類 A:
@Data
@Builder
public?class?A?{
????private?String?aName;
????private?String?aAge;
}
存在子類 B:
@Builder
@Data
@EqualsAndHashCode(callSuper?=?true)
public?class?B?extends?A?{
????private?String?bName;
????private?String?bAge;
}
使用 builder 進行初始化時,類 A 可以正常創(chuàng)建,類 B 僅可以初始化自己的屬性,父類屬性無法初始化。

二、解決:構(gòu)造方法
查閱網(wǎng)絡(luò)后,一種解決方法是利用構(gòu)造方法:
父類生成全參構(gòu)造方法 子類手動聲明全參構(gòu)造方法 將子類 @builder 注解移動全參構(gòu)造方法上,并設(shè)置 builderMethodName
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public?class?A?{
????private?String?aName;
????private?String?aAge;
}
@Data
@EqualsAndHashCode(callSuper?=?true)
@ToString(callSuper?=?true)
public?class?B?extends?A?{
????private?String?bName;
????private?String?bAge;
????@Builder(builderMethodName?=?"childBuilder")
????public?B(String?aName,?String?aAge,?String?bName,?String?bAge)?{
????????super(aName,?aAge);
????????this.bName?=?bName;
????????this.bAge?=?bAge;
????}
}
修改 Main 方法如下:
public?class?BuilderMain?{
????public?static?void?main(String[]?args)?{
????????A?xxx?=?A.builder()
????????????????.aName("xxx")
????????????????.aAge("111")
????????????????.build();
????????B?yyy?=?B.childBuilder()
????????????????.aName("xxx")
????????????????.aAge("111")
????????????????.bName("yyy")
????????????????.bAge("222")
????????????????.build();
????????System.out.println(xxx);
????????System.out.println(yyy);
????}
}
代碼運行后,能得到正確結(jié)果:
A(aName=xxx,?aAge=111)
B(super=A(aName=xxx,?aAge=111),?bName=yyy,?bAge=222)
但是這種方式弊端也很明顯:
子類調(diào)用父類的全參構(gòu)造,當父類參數(shù)數(shù)量、順序調(diào)整時,子類也需要同步調(diào)整。 如果父類參數(shù)過多,構(gòu)造方法十分不優(yōu)雅。
三、解決:SuperBuilder
Lombok 自 v1.18.2 開始,為了解決這個問題,引入了 @SuperBuilder 注解,使用該注解,就可以很容易解決這個問題。
修改代碼如下:
@Data
@SuperBuilder
public?class?A?{
????private?String?aName;
????private?String?aAge;
}
@SuperBuilder
@Data
@EqualsAndHashCode(callSuper?=?true)
@ToString(callSuper?=?true)
public?class?B?extends?A?{
????private?String?bName;
????private?String?bAge;
}
Main 方法保持不變,代碼運行后,能得到正確結(jié)果:
A(aName=xxx,?aAge=111)
B(super=A(aName=xxx,?aAge=111),?bName=yyy,?bAge=222)
另外自 v1.18.4 也給 SuperBuilder 引入了toBuilder 參數(shù),可以很方便的進行淺拷貝對象,效率雖然比手動 builder 慢一點,但也算是挺快的。
@Data
@SuperBuilder(toBuilder?=?true)
public?class?A?{
????private?String?aName;
????private?String?aAge;
}
給 B 加入屬性 C,測試對象拷貝:
@SuperBuilder(toBuilder?=?true)
@Data
@EqualsAndHashCode(callSuper?=?true)
@ToString(callSuper?=?true)
public?class?B?extends?A?{
????private?String?bName;
????private?String?bAge;
????private?C?c;
}
~
@AllArgsConstructor
public?class?C?{
????private?String?name;
}
輸出結(jié)果如下:
public?class?BuilderMain?{
????public?static?void?main(String[]?args)?{
????????B?b?=?B.builder()
????????????????.aName("xxx")
????????????????.aAge("111")
????????????????.bName("yyy")
????????????????.bAge("222")
????????????????.c(new?C("zhangsan"))
????????????????.build();
????????System.out.println(b);
????????System.out.println(b.toBuilder().build());
????}
}
B(super=A(aName=xxx,?aAge=111),?bName=yyy,?bAge=222,?c=com.github.jitwxs.demo.builder.C@52cc8049)
B(super=A(aName=xxx,?aAge=111),?bName=yyy,?bAge=222,?c=com.github.jitwxs.demo.builder.C@52cc8049)
四、彩蛋
由于 @SuperBuilder 剛引入不久,所以還是有一些 BUG 的,比如當你的 SpringBoot 版本為 2.2.3.RELEASE 時,或者你的 Lombok 版本低于 v1.18.12 時,使用上文的例子,你就會發(fā)現(xiàn)竟然無法通過編譯。

實際的原因是不支持用 B 來命名類,簡直是吐出一口老血,好在升級到 v1.18.12 版本后就修復了這個問題,可能這就是給不規(guī)范命名的人埋的坑吧,哈哈。
推薦文章
原創(chuàng)電子書
歷時整整一年總結(jié)的?Java 面試 + Java 后端技術(shù)學習指南,這是本人這幾年及校招的總結(jié),各種高頻面試題已經(jīng)全部進行總結(jié),按照章節(jié)復習即可,已經(jīng)拿到了大廠offer。
原創(chuàng)思維導圖
掃碼或者微信搜?程序員的技術(shù)圈子?回復?面試?領(lǐng)取原創(chuàng)電子書和思維導圖。



