一、Lombok簡介
Lombok項目是一個Java庫,它會自動插入您的編輯器和構建工具中,從而爲您的Java增光添彩。
永遠不要再編寫另一個getter或equals方法,一個帶有註釋的類將具有功能全面的生成器,自動執行日誌記錄變量等等。
簡而言之,就是自動幫您生成setter和getter,toString、equals等方法。
二、Lombok插件安裝
- 1.1 下載Intellij Idea Lombok插件
https://plugins.jetbrains.com/plugin/6317-lombok/versions
選擇和版本匹配的插件,否則將可能出錯;下載完成後,不需要解壓。 - 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 介紹
“樣板”是一個術語,用於描述在應用程序的許多部分中很少改動就重複的代碼。 對Java語言最常提出的批評之一是在大多數項目中都可以找到這種類型的代碼。 這個問題通常是各種庫中設計決策的結果,但由於語言本身的侷限性而加劇了這一問題。 龍目島計劃(Project Lombok)旨在通過用簡單的註釋集代替某些最嚴重的違法者。
儘管使用批註來指示用法,實現綁定甚至生成框架使用的代碼並不少見,但通常不會將其用於生成應用程序直接使用的代碼。 部分原因是因爲這樣做需要在開發時急切地處理批註。 龍目島項目就是這樣做的。 通過集成到IDE中,Project Lombok能夠注入可供開發人員立即使用的代碼。 例如,僅將@Data批註添加到數據類中(如下所示),就會在IDE中產生許多新方法:
- 3.2 Lombok 註解
對於典型的Java項目來說,將數百行代碼專門用於定義簡單數據類所需的樣板並不少見。 這些類通常包含許多字段,這些字段的getter和setter以及equals和hashCode實現。 在最簡單的情況下,Project Lombok可以將這些類簡化爲必填字段和單個**@Data**批註。
當然,最簡單的場景並不一定是開發人員每天面對的場景。 因此,Lombok項目中有許多註釋,可以對類的結構和行爲進行更精細的控制。
3.2.1 @Getter 和@Setter
@ Getter和@Setter批註分別爲字段生成getter和setter。 正確生成的getter遵循布爾屬性的約定,因此對於任何布爾字段foo而言,它都是isFoo getter方法名稱而不是getFoo。 應當注意,如果帶註釋的字段所屬的類包含與要生成的getter或setter同名的方法,則無論參數或返回類型如何,都不會生成相應的方法。
@Getter和@Setter註釋均帶有一個可選參數,以指定所生成方法的訪問級別。
註解代碼:
@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批註用於指示需要對相應成員進行快速失敗的空檢查。 當放置在Lombok爲其生成setter方法的字段上時,將生成null檢查,如果提供null值,則將導致NullPointerException。 此外,如果Lombok正在爲所屬類生成構造函數,則該字段將添加到構造函數簽名中,並且空檢查將包含在生成的構造函數代碼中。
此批註反映了在IntelliJ IDEA和FindBugs等中找到的@NotNull和@NonNull批註。 對於主題的這些變化,Lombok與註解無關。 如果Lombok遇到任何帶有名稱@NotNull或@NonNull的任何註解的成員,它將通過生成適當的相應代碼來兌現它。 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方法的實現。 默認情況下,所有非靜態字段都將以名稱/值對的形式包含在方法的輸出中。 如果需要,可以通過將註解參數includeFieldNames設置爲false來抑制在輸出中包含屬性名稱。
通過將特定字段的字段名稱包含在exclude參數中,可以從生成的方法的輸出中排除特定字段。 或者,可以使用of參數來僅列出輸出中所需的那些字段。 通過將callSuper參數設置爲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契約本質上聯繫在一起。 默認情況下,兩種方法都將考慮類中任何非靜態或瞬態的字段。 與@ToString非常相似,提供了exclude參數以防止將字段包含在生成的邏輯中。 也可以使用of參數來僅列出應考慮的那些字段。
就像@ToString一樣,此註釋也有一個callSuper參數。將其設置爲true會導致equals通過在考慮當前類中的字段之前從超類調用equals來驗證相等性。對於hashCode方法,它導致將超類的hashCode的結果併入哈希計算中。將callSuper設置爲true時,請確保父類中的equals方法正確處理實例類型檢查。如果父類檢查該類是否具有特定類型,而不僅僅是兩個對象的類相同,則可能導致不良結果。如果超類使用的是Lombok生成的equals方法,那麼這不是問題。但是,其他實現可能無法正確處理此情況。還要注意,當類僅擴展Object時,無法將callSuper設置爲true,因爲這將導致實例相等性檢查,從而使字段比較短路。這是由於生成的方法調用了Object的equals實現,如果正在比較的兩個實例不是同一實例,則返回false。結果,在這種情況下,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工具集中最常用的批註。 它結合了@ ToString,@ EqualsAndHashCode,@ Getter和@Setter的功能。 本質上,在類上使用@Data等同於使用默認的@ToString和@EqualsAndHashCode註釋類以及使用@Getter和@Setter註釋每個字段。 用@Data註釋類也會觸發Lombok的構造函數生成。 這將添加一個公共構造函數,該構造函數將任何@NonNull或final字段用作參數。 這提供了普通Java對象(POJO)所需的一切。
儘管@Data非常有用,但它不能提供與其他Lombok註釋相同的控制粒度。 爲了覆蓋默認的方法生成行爲,請使用其他Lombok批註之一對類,字段或方法進行批註,並指定必要的參數值以實現所需的效果。
@Data確實提供了可用於生成靜態工廠方法的單個參數選項。 將staticConstructor參數的值設置爲所需的方法名稱將使Lombok將生成的構造函數設爲私有,並公開具有給定名稱的靜態工廠方法。
註解代碼:
@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批註可用於確保釋放分配的資源。 當使用@Cleanup註釋局部變量時,任何後續代碼都將包裝在try / finally塊中,以確保在當前作用域的末尾調用cleanup方法。 默認情況下,@ Cleanup假定清除方法與輸入和輸出流一樣被命名爲“ close”。 但是,可以爲註釋的value參數提供不同的方法名稱。 該註釋只能使用不帶參數的清除方法。
使用@Cleanup註釋時,還需要注意一些注意事項。 如果cleanup方法引發異常,它將搶佔方法主體中引發的所有異常。 這可能導致問題被掩埋的實際原因,在選擇使用Project Lombok的資源管理時應予以考慮。 此外,隨着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 關鍵字可能會導致不幸的後果,任何從事多線程軟件開發的開發人員都可以證明。 如果是實例方法,則synchronized 關鍵字將鎖定當前對象(此對象);對於靜態方法,該關鍵字將鎖定該類對象。 這意味着開發人員無法控制代碼鎖定同一對象,從而導致死鎖。 通常建議改爲顯式地鎖定在專用於該目的的單獨對象上,並且不要以允許未經請求的鎖定的方式公開。 爲此,Project Lombok提供了@Synchronized批註。
用@Synchronized註釋實例方法將提示Lombok生成一個名爲$lock的私有鎖定字段,該方法將在執行之前在該字段上鎖定。 類似地,以相同的方式註釋靜態方法將生成一個名爲$lock的私有靜態對象,以供靜態方法以相同方式使用。 可以通過爲註釋的value參數提供字段名稱來指定其他鎖定對象。 提供字段名稱時,開發人員必須定義屬性,因爲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批註,因爲它是對已檢查異常的直接攻擊。 在使用檢查異常方面存在很多分歧,許多開發人員認爲這是一個失敗的實驗。 這些開發人員將喜歡@SneakyThrows。 處於已檢查/未檢查異常範圍另一側的那些開發人員最有可能將其視爲隱藏潛在問題。
如果在throws子句中未列出IllegalAccessException或某些父類,則拋出IllegalAccessException通常會生成“未處理的異常”錯誤:
當使用@SneakyThrows註釋時,錯誤消失了。
默認情況下,@ SneakyThrows將允許在不聲明throws子句的情況下引發任何已檢查的異常。 通過爲註釋的value參數提供可拋出的類(Class)數組,可以將其限制爲特定的一組異常。
註解代碼:
@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)的簽名,會使大多數人認爲該異常已包裝在RuntimeException中並重新拋出,但是事實並非如此。 scratchyThrow方法將永遠不會正常返回,而是將提供的throwable完全不變。
- 3.3 優缺點
與任何技術選擇一樣,使用Lombok項目既有積極的影響,也有消極的影響。 將Lombok的註釋合併到項目中可以大大減少在IDE中生成或手動編寫的樣板代碼的行數。 這樣可以減少維護開銷,減少錯誤並增加可讀性的類。
這並不是說在您的項目中使用Project Lombok註釋沒有不利之處。 Lombok項目主要旨在填補Java語言中的空白。 因此,可能會發生語言更改,從而無法使用Lombok的註釋,例如添加了一流的屬性支持。 此外,當與基於註釋的對象關係映射(ORM)框架結合使用時,數據類上註釋的數量可能開始變得笨拙。 這在很大程度上被Lombok註釋所取代的代碼量所抵消。 但是,那些避免頻繁使用註釋的人可能會選擇其他方式。
四、實例
Example Code -LombokExample.zip