和其餘語言相比, Java常常由於沒必要要的冗長被批評。 Lombok提供了一系列註解用以在後臺生成模板代碼,將其從你的類中刪除,從而有助於保持你的代碼整潔。較少的模板意味着更簡潔的代碼,更易於閱讀和維護。在本文中,我將涉及我常用的 Lombok功能,並向你展現如何使用他們生產更清晰、更簡潔的代碼。java
對方法參數進行 null 檢查一般不是一個壞主意,特別是若是該方法造成的 API被其餘開發者使用。雖然這些檢查很簡單,可是他們可能變得冗長,特別是當你有多個參數時。以下所示,額外的代碼無助於可讀性,而且可能從方法的主要目的分散注意力。設計模式
public void nonNullDemo(Employee employee, Account account) { if (employee == null) { throw new IllegalArgumentException("Employee is marked @NonNull but is null"); } if (account == null) { throw new IllegalArgumentException("Account is marked @NonNull but is null"); } // do stuff }
理想狀況下,你須要 null 檢查——沒有干擾的那種。這就是 @NonNull
發揮做用的地方。經過用@NonNull
標記參數,Lombok替你爲該參數生成 null 檢查。你的方法忽然變得更加簡潔,但沒有丟失那些安全性的 null 檢查。安全
public void nonNullDemo(@NonNull Employee employee, @NonNull Account account) { // just do stuff }
默認狀況下, Lombok會拋出 NullPointerException,若是你願意,能夠配置 Lombok拋出 IllegalArgumentException。我我的更喜歡 IllegalArgumentException,由於我認爲它更適合於對參數檢查。框架
數據類是 Lombok 真正有助於減小模板代碼的領域。在查看該選項前,思考一下咱們常常須要處理的模板種類。數據類一般包括如下一種或所有:ide
能夠經過 IDE 生成以上內容,所以問題不在於編寫他們花費的時間。問題是帶有少許成員變量的簡單類很快會變得很是冗長。讓咱們看看 Lombok 如何經過處理上述的每一項來減小混亂。函數
想一想下面的 Car 類。當生成 getter 和 setter 時,咱們會獲得接近 50 行代碼來描述一個包含 5 個成員變量的類。ui
public class Car { private String make; private String model; private String bodyType; private int yearOfManufacture; private int cubicCapacity; public String getMake() { return make; } public void setMake(String make) { this.make = make; } public String getModel() { return model; } public void setModel(String model) { this.model = model; } public String getBodyType() { return bodyType; } public void setBodyType(String bodyType) { this.bodyType = bodyType; } public int getYearOfManufacture() { return yearOfManufacture; } public void setYearOfManufacture(int yearOfManufacture) { this.yearOfManufacture = yearOfManufacture; } public int getCubicCapacity() { return cubicCapacity; } public void setCubicCapacity(int cubicCapacity) { this.cubicCapacity = cubicCapacity; } }
Lombok能夠替你生成 getter和 setter模板。經過對每一個成員變量使用 @Getter和 @Setter註解,你最終獲得一個等效的類,以下所示:this
public class Car { @Getter @Setter private String make; @Getter @Setter private String model; @Getter @Setter private String bodyType; @Getter @Setter private int yearOfManufacture; @Getter @Setter private int cubicCapacity; }
注意,你只能在非 final 成員變量上使用 @Setter,在 final成員變量上使用將致使編譯錯誤。spa
若是你須要爲每一個成員變量生成 getter和 setter,你也能夠在類級別使用 @Getter和 @Setter,以下所示。debug
@Getter @Setter public class Car { private String make; private String model; private String bodyType; private int yearOfManufacture; private int cubicCapacity; }
數據類一般包含一個構造函數,它爲每一個成員變量接受參數。IDE 爲 Car 生成的構造函數以下所示:
public class Car { @Getter @Setter private String make; @Getter @Setter private String model; @Getter @Setter private String bodyType; @Getter @Setter private int yearOfManufacture; @Getter @Setter private int cubicCapacity; public Car(String make, String model, String bodyType, int yearOfManufacture, int cubicCapacity) { super(); this.make = make; this.model = model; this.bodyType = bodyType; this.yearOfManufacture = yearOfManufacture; this.cubicCapacity = cubicCapacity; } }
咱們可使用 @AllArgsConstructor 註解實現一樣功能。 使用 @Getter 和 @Setter、 @AllArgsConstructor能夠減小模板,保持類更乾淨且更簡潔。
@AllArgsConstructor public class Car { @Getter @Setter private String make; @Getter @Setter private String model; @Getter @Setter private String bodyType; @Getter @Setter private int yearOfManufacture; @Getter @Setter private int cubicCapacity; }
還有其餘選項用於生成構造函數。 @RequiredArgsConstructor 將建立帶有每一個 final成員變量參數的構造函數, @NoArgsConstructor
將建立沒有參數的構造函數。
在你的數據類上覆蓋 toString方法是有助於記錄日誌的良好實踐。IDE 爲 Car類生成的 toString方法以下所示:
@AllArgsConstructor public class Car { @Getter @Setter private String make; @Getter @Setter private String model; @Getter @Setter private String bodyType; @Getter @Setter private int yearOfManufacture; @Getter @Setter private int cubicCapacity; @Override public String toString() { return "Car [make=" + make + ", model=" + model + ", bodyType=" + bodyType + ", yearOfManufacture=" + yearOfManufacture + ", cubicCapacity=" + cubicCapacity + "]"; } }
咱們可使用 ToString註解替換這個,以下所示:
@ToString @AllArgsConstructor public class Car { @Getter @Setter private String make; @Getter @Setter private String model; @Getter @Setter private String bodyType; @Getter @Setter private int yearOfManufacture; @Getter @Setter private int cubicCapacity; }
默認狀況下,Lombok 生成包含全部成員變量的 toString 方法。能夠經過 exclude 屬性 @ToString(exclude={"someField"},"someOtherField"})
覆蓋行爲將某些成員變量排除。
若是你正在將你的數據類和任何類型的對象比較,則須要覆蓋 equals和 hashCode 方法。對象的相等是基於業務規則定義的。舉個例子,在 Car類中,若是兩個對象有相同的 make、 model和 bodyType,我可能認爲他們是相等的。若是我使用 IDE 生成 equals 方法檢查 make、 model 和 bodyType,它看起來會是這樣:
@Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Car other = (Car) obj; if (bodyType == null) { if (other.bodyType != null) return false; } else if (!bodyType.equals(other.bodyType)) return false; if (make == null) { if (other.make != null) return false; } else if (!make.equals(other.make)) return false; if (model == null) { if (other.model != null) return false; } else if (!model.equals(other.model)) return false; return true; }
等價的 hashCode 實現以下所示:
@Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((bodyType == null) ? 0 : bodyType.hashCode()); result = prime * result + ((make == null) ? 0 : make.hashCode()); result = prime * result + ((model == null) ? 0 : model.hashCode()); return result; }
雖然 IDE 處理了繁重的工做,但咱們在類中仍然有大量的模板代碼。 Lombok容許咱們使用 @EqualsAndHashCode 類註解實現相同的功能,以下所示:
@ToString @AllArgsConstructor @EqualsAndHashCode(exclude = {"yearOfManufacture", "cubicCapacity"}) public class Car { @Getter @Setter private String make; @Getter @Setter private String model; @Getter @Setter private String bodyType; @Getter @Setter private int yearOfManufacture; @Getter @Setter private int cubicCapacity; }
默認狀況下,@EqualsAndHashCode 會建立包含全部成員變量的 equals 和 hashCode 方法。 exclude選項可用於通知 Lombok排除某些成員變量。在上面的代碼片斷中。我已經從生成的 equals和 hashCode方法中排除了 yearOfManuFacture 和 cubicCapacity。
若是你想使數據類儘量精簡,可使用 @Data 註解。 @Data 是 @Getter、 @Setter、 @ToString、 @EqualsAndHashCode 和 @RequiredArgsConstructor 的快捷方式。
@ToString @RequiredArgsConstructor @EqualsAndHashCode(exclude = {"yearOfManufacture", "cubicCapacity"}) public class Car { @Getter @Setter private String make; @Getter @Setter private String model; @Getter @Setter private String bodyType; @Getter @Setter private int yearOfManufacture; @Getter @Setter private int cubicCapacity; }
經過使用 @Data,咱們能夠將上面的類精簡以下:
@Data public class Car { private String make; private String model; private String bodyType; private int yearOfManufacture; private int cubicCapacity; }
建造者設計模式描述了一種靈活的建立對象的方式。 Lombok能夠幫你輕鬆的實現該模式。看一個使用簡單 Car 類的示例。假設咱們但願能夠建立各類 Car對象,但咱們但願在建立時設置的屬性具備靈活性。
@AllArgsConstructor public class Car { private String make; private String model; private String bodyType; private int yearOfManufacture; private int cubicCapacity; private List<LocalDate> serviceDate; }
假設咱們要建立一個 Car,但只想設置 make和 model。在 Car上使用標準的全參數構造函數意味着咱們只提供 make和 model並設置其餘參數爲 null。
Car2 car2 = new Car2("Ford", "Mustang", null, null, null, null);
這可行但並不理想,咱們必須爲咱們不感興趣的參數傳遞 null。咱們能夠建立一個只接受 make和 model的構造函數來避開這個問題。這是一個合理的解決方法,但不夠靈活。若是咱們有許多不一樣的字段排列,咱們怎麼來建立一個新 Car?最終咱們獲得了一堆不一樣的構造函數,表明了咱們能夠實例化 Car的全部可能方式。
解決該問題的一種乾淨、靈活的方式是使用建造者模式。 Lombok經過 @Builder 註解幫你實現建造者模式。當你使用 @Builder 註解 Car 類時, Lombok會執行如下操做:
CarBuilder上的每一個 setter 風格方法返回自身的實例( CarBuilder)。這容許你進行方法鏈式調用併爲對象建立提供流暢的 API。讓咱們看看它如何使用。
Car muscleCar = Car.builder().make("Ford") .model("mustang") .bodyType("coupe") .build();
如今只使用 make 和 model 建立 Car 比以前更簡潔了。只需在 Car 上簡單的調用生成的 builder 方法獲取 CarBuilder 實例,而後調用任何咱們感興趣的 setter 風格方法。最後,調用 build 建立 Car 的新實例。
另外一個值得一提的方便的註解是 @Singular。默認狀況下,Lombok 爲集合建立使用集合參數的標準的 setter 風格方法。在下面的例子中,咱們建立了新的 Car 並設置了服務日期列表。
@Builder public class Car { private String make; private String model; private String bodyType; private int yearOfManufacture; private int cubicCapacity; @Singular private List<LocalDate> serviceDate; }
向集合成員變量添加 @Singular 將提供一個額外的方法,容許你向集合添加單個項。
Car muscleCar3 = Car.builder() .make("Ford") .model("mustang") .serviceDate(LocalDate.of(2016, 5, 4)) .build();
如今咱們能夠添加單個服務日期,以下所示:
Car muscleCar3 = Car.builder() .make("Ford") .model("mustang") .serviceDate(LocalDate.of(2016, 5, 4)) .build();
這是一個有助於在建立對象期間處理集合時保持代碼簡潔的快捷方法。
Lombok另外一個偉大的功能是日誌記錄器。若是沒有 Lombok,要實例化標準的 SLF4J日誌記錄器,一般會有如下內容:
public class SomeService { private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class); public void doStuff() { log.debug("doing stuff...."); } }
這些日誌記錄器很沉重,併爲每一個須要日誌記錄的類添加了沒必要要的混亂。值得慶幸的是 Lombok提供了一個爲你建立日誌記錄器的註解。你要作的全部事情就是在類上添加註解,這樣就能夠了。
@Slf4j public class SomeService { public void doStuff() { log.debug("doing stuff...."); } }
我在這裏使用了 @SLF4J註解,但 Lombok能爲幾乎全部通用 Java日誌框架生成日誌記錄器。有關更多日誌記錄器的選項,請參閱文檔。
我很是喜歡 Lombok 的一點是它的不侵入性。若是你決定在使用如 @Getter、 @Setter 或 @ToString 時也想要本身的方法實現,你的方法將老是優先於 Lombok。它容許你在大多數時間使用 Lombok,但在你須要的時候仍有控制權。