一、背景
在實際開發中,有時候需要對子類使用lombok的 @Builder注解來使用builder模式構造該子類對象。
父類:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Parent {
private Long id;
private String name;
}
子類
import lombok.Builder;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Builder
public class Child extends Parent{
此時雖然在子類上添加了@Builder注解,但是由于子類沒有屬性,如下圖所示,無法使用builder模式。
二、分析
通過閱讀 lombok.Builder的源碼,可知 @Builder 注解不僅可以用在類上,還可以用在構造函數上。
是以嘗試如下寫法:
public class Child extends Parent {
@Builder
private Child(Long id, String name) {
super(id, name);
}
再次運作上面的單元測試,發現支援了 builder 模式,但是奇怪的是,單測不通過。
java.lang.AssertionError:
Expected :1024
Actual :null
是以我們觀察一下 Child.class 反編譯後的代碼:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
package com.chujianyun.libs.lombok;
public static Child.ChildBuilder builder() {
return new Child.ChildBuilder();
public boolean equals(final Object o) {
if (o == this) {
return true;
} else if (!(o instanceof Child)) {
return false;
} else {
Child other = (Child)o;
if (!other.canEqual(this)) {
return false;
} else {
return super.equals(o);
}
}
protected boolean canEqual(final Object other) {
return other instanceof Child;
public int hashCode() {
int result = super.hashCode();
return result;
public String toString() {
return "Child()";
public Child() {
public static class ChildBuilder {
private Long id;
private String name;
ChildBuilder() {
public Child build() {
return new Child();
public String toString() {
return "Child.ChildBuilder()";
public Child.ChildBuilder id(final Long id) {
this.id = id;
return this;
public Child.ChildBuilder name(final String name) {
this.name = name;
找到了原因,同時在子類和全參數的構造函數使用 @Builder 注解,會有 BUG,即最終的 build() 函數隻是傳回了空參的構造函數建立了一個 Child 對象,是以屬性“采用 builder 方式設定的 id 和 name” 最終“丢失”。
那麼如何解決這個問題呢?
我們再次回到@Builder 源碼的注釋上:
If a member is annotated, it must be either a constructor or a method. If a class is annotated,
* then a private constructor is generated with all fields as arguments
* (as if {@code @AllArgsConstructor(access = AccessLevel.PRIVATE)} is present
* on the class), and it is as if this constructor has been annotated with {@code @Builder} instead.
可知,将其加到類上,相當于包含所有屬性的私有構造方法,且構造方法上加上 @Builder 注解。
是以我們寫的代碼可能有沖突,我們修改如下:
最終單測通過
我們觀察一下此時編譯後的代碼:
return new Child(this.id, this.name);
return "Child.ChildBuilder(id=" + this.id + ", name=" + this.name + ")";
此時的build() 函數才是我們需要的狀态。
從編譯後的代碼我們可以清晰地看出 lombok 通過@Builder 實作的 builder模式的核心邏輯。
即構造内部類,在内部類指派屬性,build時調用含有所有屬性的構造方法建立對象。
更多細節可以仔細檢視 @Builder 注解的源碼和注釋,檢視官方的手冊
https://projectlombok.org/features/Builder三、總結
遇到詭異的問題一定不要輕易放過。
分析問題要有步驟,比如可以看源碼中是否有說明,也可以看編譯後的代碼,還可以通過反彙編等,觀察注解對類檔案作出了哪些影響。還可以去看官方手冊。