通過lombok帶你讀透Builder構(gòu)建器

通過lombok帶你讀透Builder構(gòu)建器

很久之前,我在《effective java》上看過Builder構(gòu)建器相關(guān)的內(nèi)容,但實際開發(fā)中不經(jīng)常用。后來,在項目中使用了lombok,發(fā)現(xiàn)它有一個注解“@Builder”,就是為java bean生成一個構(gòu)建器。于是,回頭重新復(fù)習(xí)了下相關(guān)知識,整理如下。

1. lombok使用樣例

// 創(chuàng)建名為Officer的java bean
@Builder
public class Officer {
    private final String id;
    private final String name;
    private final int age;
    private final String department;
}

// 調(diào)用構(gòu)建器生成Officer實例
class BuilderTest {
    public static void main(String[] args) {
        Officer officer = Officer.builder().id("00001").name("simon qi")
                .age(26).department("departmentA").build();
    }
}

2. 反編譯lombok生成的Officer.class

注意:下面請區(qū)分兩組名詞:"builder方法"和“build方法”,“構(gòu)造器”和“構(gòu)建器”。

public class Officer {
    private final String id;
    private final String name;
    private final int age;
    private final String department;

    Officer(String id, String name, int age, String department) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.department = department;
    }

    public static Officer.OfficerBuilder builder() {
        return new Officer.OfficerBuilder();
    }

    public static class OfficerBuilder {
        private String id;
        private String name;
        private int age;
        private String department;

        OfficerBuilder() {
        }

        public Officer.OfficerBuilder id(String id) {
            this.id = id;
            return this;
        }

        public Officer.OfficerBuilder name(String name) {
            this.name = name;
            return this;
        }

        public Officer.OfficerBuilder age(int age) {
            this.age = age;
            return this;
        }

        public Officer.OfficerBuilder department(String department) {
            this.department = department;
            return this;
        }

        public Officer build() {
            return new Officer(this.id, this.name, this.age, this.department);
        }

        public String toString() {
            return "Officer.OfficerBuilder(id=" + this.id + ", name=" + this.name + ", age=" + this.age + ", department=" + this.department + ")";
        }
    }
}

我們通過反編譯Officer.class,獲得上方的源碼(最好用idea自帶的反編譯器,jd-gui反編譯的源碼不全)。

我們發(fā)現(xiàn)源碼中有一個OfficerBuilder的靜態(tài)內(nèi)部類,我們在調(diào)用builder方法時,實際返回了這個靜態(tài)內(nèi)部類的實例。這個OfficerBuilder類,具有和Officer相同的成員變量,且擁有名為id,name,age和department的方法。這些以O(shè)fficer的成員變量命名的方法,都是給OfficerBuilder的成員變量賦值,并返回this。

這些方法返回this,其實就是返回調(diào)用這些方法的OfficerBuilder對象,也可稱為“返回對象本身”。通過返回對象本身,形成了方法的鏈?zhǔn)秸{(diào)用。

再看build方法,它是OfficerBuilder類的方法。它創(chuàng)建了一個新的Officer對象,并將自身的成員變量值,傳給了Officer的成員變量。所以Officer officer = Officer.builder().id("00001").name("simon qi").age(26).department("departmentA").build();的寫法,等價于下面的寫法:

Officer.OfficerBuilder officerBuilder = new Officer.OfficerBuilder();
officerBuilder.id("00001").name("simon qi").age(26).department("departmentA");
Officer officer = officerBuilder.build();

所以為什么這種模式叫“構(gòu)建器”,因為要創(chuàng)建Officer類的實例,首先要創(chuàng)建OfficerBuilder類的實例。而這個OfficerBuilder也就是構(gòu)建器,是創(chuàng)建Officer對象的一個過渡者。所以利用這種模式,會有中間實例的創(chuàng)建,會加大虛擬機(jī)內(nèi)存的消耗。

3. 只用@Builder注解的bug

我們只用@Builder注解,我發(fā)現(xiàn)lombok為Officer類生成的構(gòu)造器是“default”的(不添加權(quán)限修飾符,默認(rèn)為“default”的)。

我們之所以用構(gòu)建器模式,是希望用戶用構(gòu)建器提供的方法去創(chuàng)建實例。但“default”的構(gòu)造器,可以被同package的類調(diào)用(default限制不同package類的調(diào)用)。所以,我們需要將此構(gòu)造器設(shè)為private的。這時就需要用到“@AllArgsConstructor(access = AccessLevel.PRIVATE)”。我們這時再看反編譯后的構(gòu)造器:

private Officer(String id, String name, int age, String department) {
    this.id = id;
    this.name = name;
    this.age = age;
    this.department = department;
}

所以,使用lombok的構(gòu)建器,應(yīng)將“@Builder”和“@AllArgsConstructor(access = AccessLevel.PRIVATE)”相結(jié)合,最終寫法:

@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class Officer {
    private final String id;
    private final String name;
    private final int age;
    private final String department;
}

4. 為什么使用構(gòu)建器模式

若一個類具有大量的成員變量,我們就需要提供一個全參的構(gòu)造器或大量的set方法。這讓實例的創(chuàng)建和賦值,變得很麻煩,且不直觀。我們通過構(gòu)建器,可以讓變量的賦值變成鏈?zhǔn)秸{(diào)用,而且調(diào)用的方法名對應(yīng)著成員變量的名稱。讓對象的創(chuàng)建和賦值都變得很簡潔、直觀。

5. 鏈?zhǔn)椒椒ㄙx值,一定要用構(gòu)建器模式嗎?

不一定要用到構(gòu)建器模式,之所以使用構(gòu)建器模式,是因為我們要創(chuàng)造的對象是一個成員變量不可變的對象。

你返回去看Officer類和OfficerBuilder類,你會發(fā)現(xiàn)Officer類的成員變量都是final的,而OfficerBuilder的成員變量卻沒用final修飾。因為final修飾的成員變量,需要在實例創(chuàng)建時就把值確定下來。但在類具有大量成員變量的時候,我們是不希望用戶直接調(diào)用全參構(gòu)造器的。

所以我們使用了OfficerBuilder的中間類。這個類為了實現(xiàn)鏈?zhǔn)劫x值,才將變量設(shè)為非final的。無論你OfficerBuilder實例怎么賦值,怎么改變,當(dāng)你調(diào)用build方法時,就會返回一個成員變量不可變的Officer實例。

那如果有大量屬性,但不需要它是成員變量不可變的對象,我們還需要構(gòu)建器模式嗎?答案是,不需要,我們可以參考構(gòu)建器,把代碼賦值改成鏈?zhǔn)降募纯桑?/p>

public class Officer {
    private String id;
    private String name;
    private int age;
    private String department;

    public static Officer build() {
        return new Officer();
    }

    private Officer() {
    }

    public Officer id(String id) {
        this.id = id;
        return this;
    }

    public Officer name(String name) {
        this.name = name;
        return this;
    }

    public Officer age(int age) {
        this.age = age;
        return this;
    }

    public Officer department(String department) {
        this.department = department;
        return this;
    }
}

調(diào)用樣式:
Officer officer = Officer.build().id("00001").name("simon qi").age(26).department("departmentA");
其實這時候構(gòu)造器設(shè)為非private也行,寫成private,只是為了調(diào)用build()顯得更好看。
將構(gòu)造器設(shè)為非private,可以寫為如下形式:
Officer officer = new Officer().id("00001").name("simon qi").age(26).department("departmentA");

所以,我覺得你在使用lombok的"@Builder"注解的時候,還是要思考一下。當(dāng)你不需要成員變量不可變的時候,你完全沒必要使用構(gòu)建器模式,因為這會消耗java虛擬機(jī)的內(nèi)存。

6. 總結(jié)

所以,我一直推薦學(xué)習(xí)知識,要在項目中去學(xué)習(xí)。通過項目,去探索項目以外的知識點,才是提升自己的快捷方法。而且知識不能學(xué)死了,不能網(wǎng)上有哪些知識點,我們就只考慮這些知識點。我們要去思考一些別人不常想到的問題。比如,我們?yōu)槭裁匆弥虚g類去做過渡,這么寫的目的是什么。

將上述知識吃透,面試應(yīng)對構(gòu)建器的時候,也就得心應(yīng)手了。而且通過實戰(zhàn)去回答問題,也更能彰顯你是個愛思考的員工。

作者:永不言Qi
QQ: 591232672
e-mail:591232672@qq.com
版權(quán)聲明:轉(zhuǎn)載請保留此鏈接,不得用于商業(yè)用途。
雖然我不是最優(yōu)秀的程序員,但我還是想盡自己最大的努力,去分享一些學(xué)習(xí)心得。
如有錯誤,歡迎指正。若有幸能博得您的喜愛,歡迎關(guān)注及點贊哦。
愿我們共同進(jìn)步!
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 這是16年5月份編輯的一份比較雜亂適合自己觀看的學(xué)習(xí)記錄文檔,今天18年5月份再次想寫文章,發(fā)現(xiàn)簡書還為我保存起的...
    Jenaral閱讀 3,123評論 2 9
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,534評論 19 139
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,631評論 1 32
  • 你越是不善待你自己輕賤自己,他就越會覺得你賤,就越會覺得你所謂受的苦和傷痛都很活該。就像同樣的貨,不同的價格,人們...
    于湙遠(yuǎn)閱讀 2,405評論 0 4
  • 2月13號,我在聽完三位大牛講區(qū)塊鏈之后,才對區(qū)塊鏈有了一點模糊的了解。在覃老大講到“為什么007具有天然區(qū)...
    難以抗拒的靈魂閱讀 166評論 0 0

友情鏈接更多精彩內(nèi)容