Lombok簡介

和其餘語言相比,Java常常由於沒必要要的冗長被批評。Lombok提供了一系列註解用以在後臺生成模板代碼,將其從你的類中刪除,從而有助於保持你的代碼整潔。較少的模板意味着更簡潔的代碼,更易於閱讀和維護。在本文中,我將涉及我常用的Lombok功能,並想你展現如何使用他們生產更清晰、更簡潔的代碼。java

1.局部變量類型推斷:val 和 var

許多語言經過查看等號右側的表達式來推斷局部變量類型。儘管如今Java 10+已經支持這種功能,但在以前的版本中沒有Lombok的幫助就沒法實現。下面的代碼段展現瞭如何顯式指定局部類型:設計模式

final Map<String, Integer> map = new HashMap<>();
map.put("Joe", 21);
複製代碼

Lombok中,咱們能夠經過使用val來縮短它,以下所示:安全

val valMap = new HashMap<String, Integer>();
valMap.put("Sam", 30);
複製代碼

注意,val在背後建立了一個final且不可變的變量。若是你須要一個可變本地變量,可使用varbash

2.@NonNull

對方法參數進行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檢查。ide

public void nonNullDemo(@NonNull Employee employee, @NonNull Account account) {

    // just do stuff
}
複製代碼

默認狀況下,Lombok會拋出NullPointerException,若是你願意,能夠配置 Lombok拋出IllegalArgumentException。我我的更喜歡IllegalArgumentException,由於我認爲它更適合於對參數檢查。函數

##3.更簡潔的數據類 數據類是Lombok真正有助於減小模板代碼的領域。在查看該選項前,思考一下咱們常常須要處理的模板種類。數據類一般包括如下一種或所有:ui

  • 構造函數(有或沒有參數)
  • 私有成員變量的 getter 方法
  • 私有非 final 成員變量的 setter 方法
  • 幫助記錄日誌的 toString 方法
  • equals 和 hashCode(處理相等/集合)

能夠經過 IDE 生成以上內容,所以問題不在於編寫他們花費的時間。問題是帶有少許成員變量的簡單類很快會變得很是冗長。讓咱們看看Lombok如何經過處理上述的每一項來減小混亂。this

3.1. @Getter 和 @Setter

想一想下面的Car類。當生成gettersetter時,咱們會獲得接近 50 行代碼來描述一個包含 5 個成員變量的類。spa

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能夠替你生成gettersetter模板。經過對每一個成員變量使用 @Getter@Setter註解,你最終獲得一個等效的類,以下所示:

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成員變量上使用它將致使編譯錯誤。

若是你須要爲每一個成員變量生成gettersetter,你也能夠在類級別使用 @Getter@Setter,以下所示。

@Getter 
@Setter
public class Car {

    private String make;
    private String model;
    private String bodyType;
    private int yearOfManufacture;
    private int cubicCapacity;
}
複製代碼

3.2. @AllArgsConstructor

數據類一般包含一個構造函數,它爲每一個成員變量接受參數。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將建立沒有參數的構造函數。

3.3. @ToString

在你的數據類上覆蓋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"}) 覆蓋行爲將某些成員變量排除。

3.4. @EqualsAndHashCode

若是你正在將你的數據類和任何類型的對象比較,則須要覆蓋equalshashCode 方法。對象的相等是基於業務規則定義的。舉個例子,在Car類中,若是兩個對象有相同的makemodelbodyType,我可能認爲他們是相等的。若是我使用 IDE 生成equals方法檢查makemodelbodyType,它看起來會是這樣:

@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會建立包含全部成員變量的equalshashCode方法。exclude選項可用於通知Lombok排除某些成員變量。在上面的代碼片斷中。我已經從生成的equalshashCode方法中排除了 yearOfManuFacturecubicCapacity

3.5. @Data

若是你想使數據類儘量精簡,可使用@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;  
}
複製代碼

4. 使用 @Buidler 建立對象

建造者設計模式描述了一種靈活的建立對象的方式。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,但只想設置makemodel。在Car上使用標準的全參數構造函數意味着咱們只提供makemodel並設置其餘參數爲null

Car2 car2 = new Car2("Ford", "Mustang", null, null, null, null);
複製代碼

這可行但並不理想,咱們必須爲咱們不感興趣的參數傳遞null。咱們能夠建立一個只接受makemodel的構造函數來避開這個問題。這是一個合理的解決方法,但不夠靈活。若是咱們有許多不一樣的字段排列,咱們能夠用什麼來建立一個新Car?最終咱們獲得了一堆不一樣的構造函數,表明了咱們能夠實例化Car的全部可能方式。

解決該問題的一種乾淨、靈活的方式是使用建造者模式。Lombok經過@Builder 註解幫你實現建造者模式。當你使用@Builder註解Car類時,Lombok會執行如下操做:

  • 添加一個私有構造函數到Car
  • 建立一個靜態的CarBuilder
  • CarBuilder中爲Car中的每一個成員建立一個setter風格方法。
  • CarBuilder中添加建立Car的新實例的建造方法。

CarBuilder上的每一個setter風格方法返回自身的實例(CarBuilder)。這容許你進行方法鏈式調用併爲對象建立提供流暢的 API。讓咱們看看它如何使用。

Car muscleCar = Car.builder().make("Ford")
                             .model("mustang")
                             .bodyType("coupe")
                             .build();
複製代碼

如今只使用makemodel建立Car比以前更簡潔了。只需在Car上簡單的調用生成的builder方法獲取CarBuilder實例,而後調用任何咱們感興趣的setter風格方法。最後,調用build建立Car的新實例。

另外一個值得一提的方便的註解是@Singular。默認狀況下,Lombok 爲集合建立使用集合參數的標準的 setter 風格方法。在下面的例子中,咱們建立了新的 Car並設置了服務日期列表。

Car muscleCar = Car.builder().make("Ford")
                   .model("mustang")
                   .serviceDate(Arrays.asList(LocalDate.of(2016, 5, 4)))
                   .build();
複製代碼

向集合成員變量添加@Singular將提供一個額外的方法,容許你向集合添加單個項。

@Builder
public class Car {

    private String make;
    private String model;
    private String bodyType;
    private int yearOfManufacture;
    private int cubicCapacity;  
    @Singular
    private List<LocalDate> serviceDate;
}
複製代碼

如今咱們能夠添加單個服務日期,以下所示:

Car muscleCar3 = Car.builder()
                    .make("Ford")
                    .model("mustang")
                    .serviceDate(LocalDate.of(2016, 5, 4))
                    .build();
複製代碼

這是一個有助於在建立對象期間處理集合時保持代碼簡潔的快捷方法。

5.日誌

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日誌框架生成日誌記錄器。有關更多日誌記錄器的選項,請參閱文檔。

6.Lombok給你控制權

我很是喜歡Lombok的一點是它的不侵入性。。若是你決定在使用如@Getter@Setter@ToString時也想要本身的方法實現,你的方法將老是優先於 Lombok。它容許你在大多數時間使用Lombok,但在你須要的時候仍有控制權。

7.寫得更少,作得更多

在過去的 4 到 5 年裏,我幾乎在每一個項目中都使用了Lombok。我喜歡它,由於它減小了雜亂,最終獲得了更乾淨、更簡潔、更易閱讀的代碼。它不必定爲你節省大量時間,由於它生成的代碼能夠由 IDE 自動生成。話雖如此,我認爲更乾淨的代碼的好處不只僅是將其添加到Java堆棧中。

8. 延展閱讀

我已經介紹了我常用的Lombok功能,但還有不少我沒有講到。若是你喜歡目前爲止所看到的,並但願瞭解更多,請繼續閱讀 Lombok 文檔。

原文連接:dzone.com/articles/in…

做者:Brian Hannaway

譯者:Darren Luo

推薦關注公衆號:鍋外的大佬

每日推送國外優秀的技術翻譯文章,勵志幫助國內的開發者更好地成長!

相關文章
相關標籤/搜索