欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

lombok?子類中如何使用@Builder問題

 更新時間:2022年09月21日 10:03:13   作者:lqadam  
這篇文章主要介紹了lombok?子類中如何使用@Builder問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

lombok子類中如何使用@Builder

lombok大家都知道,在使用POJO過程中,它給我們帶來了很多便利,省下大量寫get、set方法、構(gòu)造器、equal、toString方法的時間。除此之外,通過@Builder注解,lombok還可以方便的時間建造者模式。

但是,在使用@Builder過程中,我發(fā)現(xiàn)了一問題:子類的Builder對象沒有父類的屬性。這在使用上造成了一定的問題。

幾番搜索,對于這個問題,找到了如下解法,解法的鏈接會放到文末。

1. 對于父類,使用@AllArgsConstructor注解

2. 對于子類,手動編寫全參數(shù)構(gòu)造器,內(nèi)部調(diào)用父類全參數(shù)構(gòu)造器,在子類全參數(shù)構(gòu)造器上使用@Builder注解

通過這種方式,子類Builder對象可以使用父類的所有私有屬性。

但是這種解法也有兩個副作用:

1. 因為使用AllArgsConstructor注解,父類構(gòu)造函數(shù)字段的順序由聲明字段的順序決定,如果子類構(gòu)造函數(shù)傳參的時候順序不一致,字段類型還一樣的話,出了錯不好發(fā)現(xiàn)

2. 如果父類字段有增減,所有子類的構(gòu)造器都要修改

雖然有這兩個副作用,但是這種解法是我找到的唯一一種解決子類使用@Builder,能使用父類屬性的方式。

參考博客:

Lombok’s @Builder annotation and inheritance

副作用見博客評論

另,這個博主對lombok使用很有心得,我閑看還看到他另一篇涉及到@Builder的文章,將如何在使用@Builder的模式中,加入字段的默認值。因為使用了建造者模式,那么一般在類內(nèi)聲明字段的時候給字段默認值的方式就是無效的,需要在建造者上動手腳。

方式是:

1. 自定義靜態(tài)內(nèi)部類作為建造者,賦予默認值,再使用@Builder注解,這個時候lombok會補全已有的建造者類,進而使用默認值

2. 更新的lombok有@Builder.Default聲明,注解在需要默認值的字段上即可。

在評論區(qū)也有這種方式的副作用討論,可以一看。鏈接是:

Using Lombok’s @Builder annotation with default values

子類使用lombok的@Builder注解正確姿勢

在實際開發(fā)中,有時候需要對子類使用lombok的 @Builder注解來使用builder模式構(gòu)造該子類對象。

父類

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.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
 
@EqualsAndHashCode(callSuper = true)
@Data
@NoArgsConstructor
@Builder
public class Child extends Parent{
 
 
}

此時雖然在子類上添加了@Builder注解,但是由于子類沒有屬性,如下圖所示,無法使用builder模式。

分析一下

通過閱讀 lombok.Builder的源碼,可知 @Builder 注解不僅可以用在類上,還可以用在構(gòu)造函數(shù)上。

因此嘗試如下寫法:

@EqualsAndHashCode(callSuper = true)
@Data
@NoArgsConstructor
@Builder
public class Child extends Parent {
 
    @Builder
    private Child(Long id, String name) {
        super(id, name);
    }
}

再次運行上面的單元測試,發(fā)現(xiàn)支持了 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 class Child extends Parent {
    private Child(Long id, String name) {
        super(id, name);
    }
 
    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;
            return this;
        }
    }
}

找到了原因,同時在子類和全參數(shù)的構(gòu)造函數(shù)使用 @Builder 注解,會有 BUG,即最終的 build() 函數(shù)只是返回了空參的構(gòu)造函數(shù)創(chuàng)建了一個 Child 對象,因此屬性“采用 builder 方式設(shè)置的 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.

可知,將其加到類上,相當(dāng)于包含所有屬性的私有構(gòu)造方法,且構(gòu)造方法上加上 @Builder 注解。

因此我們寫的代碼可能有沖突,我們修改如下:

import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
 
@EqualsAndHashCode(callSuper = true)
@Data
@NoArgsConstructor
public class Child extends Parent {
 
    @Builder
    private Child(Long id, String name) {
        super(id, name);
    }
}

最終單測通過

我們觀察一下此時編譯后的代碼:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
 
package com.chujianyun.libs.lombok;
 
public class Child extends Parent {
    private Child(Long id, String name) {
        super(id, name);
    }
 
    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.ChildBuilder id(final Long id) {
            this.id = id;
            return this;
        }
 
        public Child.ChildBuilder name(final String name) {
            this.name = name;
            return this;
        }
 
        public Child build() {
            return new Child(this.id, this.name);
        }
 
        public String toString() {
            return "Child.ChildBuilder(id=" + this.id + ", name=" + this.name + ")";
        }
    }
}

此時的build() 函數(shù)才是我們需要的狀態(tài)。

從編譯后的代碼我們可以清晰地看出 lombok 通過@Builder 實現(xiàn)的 builder模式的核心邏輯。

即構(gòu)造內(nèi)部類,在內(nèi)部類賦值屬性,build時調(diào)用含有所有屬性的構(gòu)造方法創(chuàng)建對象。

更多細節(jié)可以仔細查看 @Builder 注解的源碼和注釋,查看官方的手冊 https://projectlombok.org/features/Builder

總結(jié):

遇到詭異的問題一定不要輕易放過。分析問題要有步驟,比如可以看源碼中是否有說明,也可以看編譯后的代碼,還可以通過反匯編等,觀察注解對類文件作出了哪些影響。還可以去看官方手冊。

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • IntelliJ IDEA窗口組件具體操作方法

    IntelliJ IDEA窗口組件具體操作方法

    IDEA剛接觸不久,各種常用工具窗口找不到,不小心關(guān)掉不知道從哪里打開,今天小編給大家分享這個問題的解決方法,感興趣的朋友一起看看吧
    2021-09-09
  • Java Class 加密工具 ClassFinal詳解

    Java Class 加密工具 ClassFinal詳解

    ClassFinal 是一款 java class 文件安全加密工具,支持直接加密jar包或war包,無需修改任何項目代碼,兼容spring-framework;可避免源碼泄漏或字節(jié)碼被反編譯,這篇文章主要介紹了Java Class 加密工具 ClassFinal,需要的朋友可以參考下
    2023-03-03
  • SpringBoot整合canal實現(xiàn)數(shù)據(jù)緩存一致性解決方案

    SpringBoot整合canal實現(xiàn)數(shù)據(jù)緩存一致性解決方案

    canal主要用途是基于?MySQL?數(shù)據(jù)庫增量日志解析,提供增量數(shù)據(jù)訂閱和消費,canal是借助于MySQL主從復(fù)制原理實現(xiàn),本文將給大家介紹SpringBoot整合canal實現(xiàn)數(shù)據(jù)緩存一致性解決方案,需要的朋友可以參考下
    2024-03-03
  • java遞歸實現(xiàn)樹形結(jié)構(gòu)數(shù)據(jù)完整案例

    java遞歸實現(xiàn)樹形結(jié)構(gòu)數(shù)據(jù)完整案例

    遞歸算法的代碼比較簡潔,可讀性較好;但是在實際的業(yè)務(wù)處理中會出現(xiàn)多次的重復(fù)調(diào)用,如果處理不好,很容易出現(xiàn)StackOverflowError報錯,這篇文章主要給大家介紹了關(guān)于java遞歸實現(xiàn)樹形結(jié)構(gòu)數(shù)據(jù)的相關(guān)資料,需要的朋友可以參考下
    2023-04-04
  • windows下 jdk1.7安裝教程圖解

    windows下 jdk1.7安裝教程圖解

    java編程的初學(xué)者在開始編碼前都會遇到一個難題,那就是jdk1.7環(huán)境變量配置怎么操作,怎么安裝,針對這個難題,小編特地為大家整理相關(guān)教程,不了解的朋友可以前往查看使用
    2018-05-05
  • java工具類SendEmailUtil實現(xiàn)發(fā)送郵件

    java工具類SendEmailUtil實現(xiàn)發(fā)送郵件

    這篇文章主要為大家詳細介紹了java工具類SendEmailUtil實現(xiàn)發(fā)送郵件,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-02-02
  • 使用SpringBoot獲取resources文件路徑

    使用SpringBoot獲取resources文件路徑

    這篇文章主要介紹了使用SpringBoot獲取resources文件路徑,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • Java并發(fā)編程變量可見性避免指令重排使用詳解

    Java并發(fā)編程變量可見性避免指令重排使用詳解

    這篇文章主要為大家介紹了Java并發(fā)編程變量可見性避免指令重排使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-11-11
  • 深入理解Java設(shè)計模式之備忘錄模式

    深入理解Java設(shè)計模式之備忘錄模式

    這篇文章主要介紹了JAVA設(shè)計模式之備忘錄模式的的相關(guān)資料,文中示例代碼非常詳細,供大家參考和學(xué)習(xí),感興趣的朋友可以了解
    2021-11-11
  • Java 中Timer和TimerTask 定時器和定時任務(wù)使用的例子

    Java 中Timer和TimerTask 定時器和定時任務(wù)使用的例子

    這篇文章主要介紹了Java 中Timer和TimerTask 定時器和定時任務(wù)使用的例子,非常具有實用價值,需要的朋友可以參考下
    2017-05-05

最新評論