Lombok的使用教程

一池颈、Lombok簡介

Lombok項目是一個Java庫芙贫,它會自動插入您的編輯器和構(gòu)建工具中,從而為您的Java增光添彩豌鸡。
永遠不要再編寫另一個getter或equals方法,一個帶有注釋的類將具有功能全面的生成器段标,自動執(zhí)行日志記錄變量等等涯冠。

簡而言之,就是自動幫您生成setter和getter逼庞,toString蛇更、equals等方法。

二、Lombok插件安裝

  • 1.1 下載Intellij Idea Lombok插件
    https://plugins.jetbrains.com/plugin/6317-lombok/versions
    Lombok與Idea版本對應(yīng)表

    選擇和版本匹配的插件派任,否則將可能出錯共耍;下載完成后,不需要解壓吨瞎。
  • 1.2 Intellij Idea 安裝離線插件


    從桌面安裝插件

    安裝完成后痹兜,重新啟動即可。

二颤诀、Lombok依賴配置

maven倉庫查看版本并在pom.xml中添加依賴

<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.12</version>
    <scope>provided</scope>
</dependency>

三字旭、Java中使用Lombok

  • 3.1 介紹
    “樣板”是一個術(shù)語,用于描述在應(yīng)用程序的許多部分中很少改動就重復(fù)的代碼崖叫。 對Java語言最常提出的批評之一是在大多數(shù)項目中都可以找到這種類型的代碼遗淳。 這個問題通常是各種庫中設(shè)計決策的結(jié)果奔害,但由于語言本身的局限性而加劇了這一問題祝钢。 龍目島計劃(Project Lombok)旨在通過用簡單的注釋集代替某些最嚴重的違法者丽涩。
    盡管使用批注來指示用法监婶,實現(xiàn)綁定甚至生成框架使用的代碼并不少見筛圆,但通常不會將其用于生成應(yīng)用程序直接使用的代碼卡骂。 部分原因是因為這樣做需要在開發(fā)時急切地處理批注崔慧。 龍目島項目就是這樣做的狠半。 通過集成到IDE中宰翅,Project Lombok能夠注入可供開發(fā)人員立即使用的代碼弃甥。 例如,僅將@Data批注添加到數(shù)據(jù)類中(如下所示)汁讼,就會在IDE中產(chǎn)生許多新方法:


    Data_Annotation.png
  • 3.2 Lombok 注解
    對于典型的Java項目來說淆攻,將數(shù)百行代碼專門用于定義簡單數(shù)據(jù)類所需的樣板并不少見。 這些類通常包含許多字段嘿架,這些字段的getter和setter以及equals和hashCode實現(xiàn)瓶珊。 在最簡單的情況下,Project Lombok可以將這些類簡化為必填字段和單個@Data批注耸彪。
    當(dāng)然伞芹,最簡單的場景并不一定是開發(fā)人員每天面對的場景。 因此搜囱,Lombok項目中有許多注釋丑瞧,可以對類的結(jié)構(gòu)和行為進行更精細的控制。
    3.2.1 @Getter 和@Setter
    @ Getter和@Setter批注分別為字段生成getter和setter蜀肘。 正確生成的getter遵循布爾屬性的約定绊汹,因此對于任何布爾字段foo而言,它都是isFoo getter方法名稱而不是getFoo扮宠。 應(yīng)當(dāng)注意西乖,如果帶注釋的字段所屬的類包含與要生成的getter或setter同名的方法狐榔,則無論參數(shù)或返回類型如何,都不會生成相應(yīng)的方法获雕。
    @Getter和@Setter注釋均帶有一個可選參數(shù)薄腻,以指定所生成方法的訪問級別。
    注解代碼:
@Getter
@Setter 
private boolean employed = true;
@Setter(AccessLevel.PROTECTED) 
private String name;

等價于原Java代碼:

    private boolean employed = true;
    private String name;
     
    public boolean isEmployed() {
        return employed;
    }
     
    public void setEmployed(final boolean employed) {
        this.employed = employed;
    }
     
    protected void setName(final String name) {
        this.name = name;
    }

3.2.2 @NonNull
@NonNull批注用于指示需要對相應(yīng)成員進行快速失敗的空檢查届案。 當(dāng)放置在Lombok為其生成setter方法的字段上時庵楷,將生成null檢查,如果提供null值楣颠,則將導(dǎo)致NullPointerException尽纽。 此外,如果Lombok正在為所屬類生成構(gòu)造函數(shù)童漩,則該字段將添加到構(gòu)造函數(shù)簽名中弄贿,并且空檢查將包含在生成的構(gòu)造函數(shù)代碼中。

此批注反映了在IntelliJ IDEA和FindBugs等中找到的@NotNull和@NonNull批注矫膨。 對于主題的這些變化差凹,Lombok與注解無關(guān)。 如果Lombok遇到任何帶有名稱@NotNull或@NonNull的任何注解的成員侧馅,它將通過生成適當(dāng)?shù)南鄳?yīng)代碼來兌現(xiàn)它危尿。 Lombok項目的作者進一步評論說,如果將這種類型的注解添加到Java中施禾,則Lombok版本將被刪除脚线。
注解代碼:

@Getter 
@Setter 
@NonNull
private List<Person> members;

等價于原Java代碼:

    @NonNull
    private List<Person> members;
     
    public Family(@NonNull final List<Person> members) {
        if (members == null) throw new java.lang.NullPointerException("members");
        this.members = members;
    }
     
    @NonNull
    public List<Person> getMembers() {
        return members;
    }
     
    public void setMembers(@NonNull final List<Person> members) {
        if (members == null) throw new java.lang.NullPointerException("members");
        this.members = members;
    }

3.2.3 @ToString
該注釋生成toString方法的實現(xiàn)。 默認情況下弥搞,所有非靜態(tài)字段都將以名稱/值對的形式包含在方法的輸出中。 如果需要渠旁,可以通過將注解參數(shù)includeFieldNames設(shè)置為false來抑制在輸出中包含屬性名稱攀例。
通過將特定字段的字段名稱包含在exclude參數(shù)中,可以從生成的方法的輸出中排除特定字段顾腊。 或者粤铭,可以使用of參數(shù)來僅列出輸出中所需的那些字段。 通過將callSuper參數(shù)設(shè)置為true杂靶,還可以包含超類的toString方法的輸出梆惯。
注解代碼:

    @ToString(callSuper=true,exclude="someExcludedField")
    public class Foo extends Bar {
        private boolean someBoolean = true;
        private String someStringField;
        private float someExcludedField;
    }

等價于原Java代碼:

    public class Foo extends Bar {
        private boolean someBoolean = true;
        private String someStringField;
        private float someExcludedField;
     
        @java.lang.Override
        public java.lang.String toString() {
            return "Foo(super=" + super.toString() +
                ", someBoolean=" + someBoolean +
                ", someStringField=" + someStringField + ")";
        }
    }

3.2.4 @EqualsAndHashCode
這個類級別的注釋將使Lombok生成equals和hashCode方法,因為兩者通過hashCode契約本質(zhì)上聯(lián)系在一起吗垮。 默認情況下垛吗,兩種方法都將考慮類中任何非靜態(tài)或瞬態(tài)的字段。 與@ToString非常相似烁登,提供了exclude參數(shù)以防止將字段包含在生成的邏輯中怯屉。 也可以使用of參數(shù)來僅列出應(yīng)考慮的那些字段。

就像@ToString一樣,此注釋也有一個callSuper參數(shù)锨络。將其設(shè)置為true會導(dǎo)致equals通過在考慮當(dāng)前類中的字段之前從超類調(diào)用equals來驗證相等性赌躺。對于hashCode方法,它導(dǎo)致將超類的hashCode的結(jié)果并入哈希計算中羡儿。將callSuper設(shè)置為true時礼患,請確保父類中的equals方法正確處理實例類型檢查。如果父類檢查該類是否具有特定類型掠归,而不僅僅是兩個對象的類相同讶泰,則可能導(dǎo)致不良結(jié)果。如果超類使用的是Lombok生成的equals方法拂到,那么這不是問題痪署。但是,其他實現(xiàn)可能無法正確處理此情況兄旬。還要注意狼犯,當(dāng)類僅擴展Object時,無法將callSuper設(shè)置為true领铐,因為這將導(dǎo)致實例相等性檢查悯森,從而使字段比較短路。這是由于生成的方法調(diào)用了Object的equals實現(xiàn)绪撵,如果正在比較的兩個實例不是同一實例瓢姻,則返回false。結(jié)果音诈,在這種情況下幻碱,Lombok將生成編譯時錯誤。
注解代碼:

@EqualsAndHashCode(callSuper=true,exclude={"address","city","state","zip"})
public class Person extends SentientBeing {
    enum Gender { Male, Female }
 
    @NonNull private String name;
    @NonNull private Gender gender; 
    private String ssn;
    private String address;
    private String city;
    private String state;
    private String zip;
}

等價于原Java代碼:

    public class Person extends SentientBeing {
     
        enum Gender {
            /*public static final*/ Male /* = new Gender() */,
            /*public static final*/ Female /* = new Gender() */;
        }
        @NonNull
        private String name;
        @NonNull
        private Gender gender;
        private String ssn;
        private String address;
        private String city;
        private String state;
        private String zip;
     
        @java.lang.Override
        public boolean equals(final java.lang.Object o) {
            if (o == this) return true;
            if (o == null) return false;
            if (o.getClass() != this.getClass()) return false;
            if (!super.equals(o)) return false;
            final Person other = (Person)o;
            if (this.name == null ? other.name != null : !this.name.equals(other.name)) return false;
            if (this.gender == null ? other.gender != null : !this.gender.equals(other.gender)) return false;
            if (this.ssn == null ? other.ssn != null : !this.ssn.equals(other.ssn)) return false;
            return true;
        }
     
        @java.lang.Override
        public int hashCode() {
            final int PRIME = 31;
            int result = 1;
            result = result * PRIME + super.hashCode();
            result = result * PRIME + (this.name == null ? 0 : this.name.hashCode());
            result = result * PRIME + (this.gender == null ? 0 : this.gender.hashCode());
            result = result * PRIME + (this.ssn == null ? 0 : this.ssn.hashCode());
            return result;
        }
    }

3.2.5 @Data
@Data批注可能是Project Lombok工具集中最常用的批注细溅。 它結(jié)合了@ ToString褥傍,@ EqualsAndHashCode,@ Getter和@Setter的功能喇聊。 本質(zhì)上恍风,在類上使用@Data等同于使用默認的@ToString和@EqualsAndHashCode注釋類以及使用@Getter和@Setter注釋每個字段。 用@Data注釋類也會觸發(fā)Lombok的構(gòu)造函數(shù)生成誓篱。 這將添加一個公共構(gòu)造函數(shù)朋贬,該構(gòu)造函數(shù)將任何@NonNull或final字段用作參數(shù)。 這提供了普通Java對象(POJO)所需的一切窜骄。

盡管@Data非常有用锦募,但它不能提供與其他Lombok注釋相同的控制粒度。 為了覆蓋默認的方法生成行為啊研,請使用其他Lombok批注之一對類御滩,字段或方法進行批注鸥拧,并指定必要的參數(shù)值以實現(xiàn)所需的效果。

@Data確實提供了可用于生成靜態(tài)工廠方法的單個參數(shù)選項削解。 將staticConstructor參數(shù)的值設(shè)置為所需的方法名稱將使Lombok將生成的構(gòu)造函數(shù)設(shè)為私有富弦,并公開具有給定名稱的靜態(tài)工廠方法。
注解代碼:

    @Data(staticConstructor="of")
    public class Company {
        private final Person founder;
        private String name;
        private List<Person> employees;
    }

等價于原Java代碼:

    public class Company {
        private final Person founder;
        private String name;
        private List<Person> employees;
     
        private Company(final Person founder) {
            this.founder = founder;
        }
     
        public static Company of(final Person founder) {
            return new Company(founder);
        }
     
        public Person getFounder() {
            return founder;
        }
     
        public String getName() {
            return name;
        }
     
        public void setName(final String name) {
            this.name = name;
        }
     
        public List<Person> getEmployees() {
            return employees;
        }
     
        public void setEmployees(final List<Person> employees) {
            this.employees = employees;
        }
     
        @java.lang.Override
        public boolean equals(final java.lang.Object o) {
            if (o == this) return true;
            if (o == null) return false;
            if (o.getClass() != this.getClass()) return false;
            final Company other = (Company)o;
            if (this.founder == null ? other.founder != null : !this.founder.equals(other.founder)) return false;
            if (this.name == null ? other.name != null : !this.name.equals(other.name)) return false;
            if (this.employees == null ? other.employees != null : !this.employees.equals(other.employees)) return false;
            return true;
        }
     
        @java.lang.Override
        public int hashCode() {
            final int PRIME = 31;
            int result = 1;
            result = result * PRIME + (this.founder == null ? 0 : this.founder.hashCode());
            result = result * PRIME + (this.name == null ? 0 : this.name.hashCode());
            result = result * PRIME + (this.employees == null ? 0 : this.employees.hashCode());
            return result;
        }
     
        @java.lang.Override
        public java.lang.String toString() {
            return "Company(founder=" + founder + ", name=" + name + ", employees=" + employees + ")";
        }
    }

3.2.6 @Cleanup
@Cleanup批注可用于確保釋放分配的資源氛驮。 當(dāng)使用@Cleanup注釋局部變量時腕柜,任何后續(xù)代碼都將包裝在try / finally塊中,以確保在當(dāng)前作用域的末尾調(diào)用cleanup方法矫废。 默認情況下盏缤,@ Cleanup假定清除方法與輸入和輸出流一樣被命名為“ close”。 但是蓖扑,可以為注釋的value參數(shù)提供不同的方法名稱唉铜。 該注釋只能使用不帶參數(shù)的清除方法。

使用@Cleanup注釋時律杠,還需要注意一些注意事項潭流。 如果cleanup方法引發(fā)異常,它將搶占方法主體中引發(fā)的所有異常柜去。 這可能導(dǎo)致問題被掩埋的實際原因灰嫉,在選擇使用Project Lombok的資源管理時應(yīng)予以考慮。 此外嗓奢,隨著Java 7中自動資源管理的興起讼撒,這個特定的注釋可能相對較短。
注解代碼:

    public void testCleanUp() {
        try {
            @Cleanup 
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            baos.write(new byte[] {'Y','e','s'});
            System.out.println(baos.toString());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

等價于原Java代碼:

    public void testCleanUp() {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            try {
                baos.write(new byte[]{'Y', 'e', 's'});
                System.out.println(baos.toString());
            } finally {
                baos.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

3.2.6 @Synchronized
在方法上使用synchronized 關(guān)鍵字可能會導(dǎo)致不幸的后果股耽,任何從事多線程軟件開發(fā)的開發(fā)人員都可以證明根盒。 如果是實例方法,則synchronized 關(guān)鍵字將鎖定當(dāng)前對象(此對象)豺谈;對于靜態(tài)方法郑象,該關(guān)鍵字將鎖定該類對象。 這意味著開發(fā)人員無法控制代碼鎖定同一對象茬末,從而導(dǎo)致死鎖。 通常建議改為顯式地鎖定在專用于該目的的單獨對象上盖矫,并且不要以允許未經(jīng)請求的鎖定的方式公開丽惭。 為此,Project Lombok提供了@Synchronized批注辈双。

用@Synchronized注釋實例方法將提示Lombok生成一個名為$lock的私有鎖定字段责掏,該方法將在執(zhí)行之前在該字段上鎖定。 類似地湃望,以相同的方式注釋靜態(tài)方法將生成一個名為$lock的私有靜態(tài)對象换衬,以供靜態(tài)方法以相同方式使用痰驱。 可以通過為注釋的value參數(shù)提供字段名稱來指定其他鎖定對象。 提供字段名稱時瞳浦,開發(fā)人員必須定義屬性担映,因為Lombok不會生成該屬性。
注解代碼:

    private DateFormat format = new SimpleDateFormat("MM-dd-YYYY");
     
    @Synchronized
    public String synchronizedFormat(Date date) {
        return format.format(date);
    }

等價于原Java代碼:



    private final java.lang.Object $lock = new java.lang.Object[0];
    private DateFormat format = new SimpleDateFormat("MM-dd-YYYY");
     
    public String synchronizedFormat(Date date) {
        synchronized ($lock) {
            return format.format(date);
        }
    }

3.2.8 @SneakyThrows
@SneakyThrows可能是批評者最多的Project Lombok批注叫潦,因為它是對已檢查異常的直接攻擊蝇完。 在使用檢查異常方面存在很多分歧,許多開發(fā)人員認為這是一個失敗的實驗矗蕊。 這些開發(fā)人員將喜歡@SneakyThrows短蜕。 處于已檢查/未檢查異常范圍另一側(cè)的那些開發(fā)人員最有可能將其視為隱藏潛在問題。

如果在throws子句中未列出IllegalAccessException或某些父類傻咖,則拋出IllegalAccessException通常會生成“未處理的異撑竽В”錯誤:


Unhandled_Exception.png

當(dāng)使用@SneakyThrows注釋時,錯誤消失了卿操。


Sneaky_Throws.png

默認情況下警检,@ SneakyThrows將允許在不聲明throws子句的情況下引發(fā)任何已檢查的異常。 通過為注釋的value參數(shù)提供可拋出的類(Class)數(shù)組硬纤,可以將其限制為特定的一組異常解滓。
注解代碼:
    @SneakyThrows
    public void testSneakyThrows() {
        throw new IllegalAccessException();
    }

等價于原Java代碼:

    public void testSneakyThrows() {
        try {
            throw new IllegalAccessException();
        } catch (java.lang.Throwable $ex) {
            throw lombok.Lombok.sneakyThrow($ex);
        }

查看上面的代碼和Lombok.sneakyThrow(Throwable)的簽名,會使大多數(shù)人認為該異常已包裝在RuntimeException中并重新拋出筝家,但是事實并非如此洼裤。 scratchyThrow方法將永遠不會正常返回,而是將提供的throwable完全不變溪王。

  • 3.3 優(yōu)缺點
    與任何技術(shù)選擇一樣腮鞍,使用Lombok項目既有積極的影響,也有消極的影響莹菱。 將Lombok的注釋合并到項目中可以大大減少在IDE中生成或手動編寫的樣板代碼的行數(shù)移国。 這樣可以減少維護開銷,減少錯誤并增加可讀性的類道伟。
    這并不是說在您的項目中使用Project Lombok注釋沒有不利之處迹缀。 Lombok項目主要旨在填補Java語言中的空白。 因此蜜徽,可能會發(fā)生語言更改祝懂,從而無法使用Lombok的注釋,例如添加了一流的屬性支持拘鞋。 此外砚蓬,當(dāng)與基于注釋的對象關(guān)系映射(ORM)框架結(jié)合使用時,數(shù)據(jù)類上注釋的數(shù)量可能開始變得笨拙盆色。 這在很大程度上被Lombok注釋所取代的代碼量所抵消灰蛙。 但是祟剔,那些避免頻繁使用注釋的人可能會選擇其他方式。

四摩梧、實例

Example Code -LombokExample.zip

五物延、參考

Reducing Boilerplate Code with Project Lombok

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市障本,隨后出現(xiàn)的幾起案子教届,更是在濱河造成了極大的恐慌,老刑警劉巖驾霜,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件案训,死亡現(xiàn)場離奇詭異,居然都是意外死亡粪糙,警方通過查閱死者的電腦和手機强霎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蓉冈,“玉大人城舞,你說我怎么就攤上這事∧穑” “怎么了家夺?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長伐弹。 經(jīng)常有香客問我拉馋,道長,這世上最難降的妖魔是什么惨好? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任煌茴,我火速辦了婚禮,結(jié)果婚禮上日川,老公的妹妹穿的比我還像新娘蔓腐。我一直安慰自己,他們只是感情好龄句,可當(dāng)我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布回论。 她就那樣靜靜地躺著,像睡著了一般分歇。 火紅的嫁衣襯著肌膚如雪透葛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天卿樱,我揣著相機與錄音,去河邊找鬼硫椰。 笑死繁调,一個胖子當(dāng)著我的面吹牛萨蚕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蹄胰,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼岳遥,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了裕寨?” 一聲冷哼從身側(cè)響起浩蓉,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎宾袜,沒想到半個月后捻艳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡庆猫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年认轨,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片月培。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡嘁字,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出杉畜,到底是詐尸還是另有隱情纪蜒,我是刑警寧澤,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布此叠,位于F島的核電站纯续,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏拌蜘。R本人自食惡果不足惜杆烁,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望简卧。 院中可真熱鬧兔魂,春花似錦、人聲如沸举娩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽铜涉。三九已至智玻,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間芙代,已是汗流浹背吊奢。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留纹烹,地道東北人页滚。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓召边,卻偏偏與公主長得像,于是被迫代替她去往敵國和親裹驰。 傳聞我的和親對象是個殘疾皇子隧熙,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內(nèi)容