2019年4月22日

Project Lombok

Lombok Project 是一個很小的 Java Annotation Library,目的是消除 Java 中一直不斷發生的重複程式碼,以 Java Bean 為例,最常見的就是需要在撰寫 class member 後,還需要寫一堆重複的 getter/setter,雖然 IDE 可以協助產生這些程式碼,但遇到欄位名稱修改,或是增刪欄位時,還是會造成困擾。

Installation

參考 Lombok 網頁的 Install 部分,基本上針對 project,就參考 Build tools 的部分,以我們使用來說,是看 maven。

在 Maven POM 裡面加上這個 dependency library,就可以使用了。

<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.2</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

另外,通常 project 會搭配某個 IDE 進行開發,我們在 Intellij IDEA 安裝 Lombok plugin。

  • 在 Plugins 功能中,Browse repositories
  • 然後搜尋 Lombok Plugin
  • Install Plugin
  • Restart IDEA

因為 Lombok 只會將程式碼產生在編譯後的 class 裡面,在 IDE 必須要搭配 Plugin,才能偵測到 Lombok Plugin,並在編譯前了解 Lombok 會產生的 method/code,安裝了 Plugin 才不會看到編譯 Error 的警告。

Features

@Getter @Setter
@Getter
@Setter
private boolean employed = true;

@Setter(AccessLevel.PROTECTED)
private String name;

就等於以下的 Java Code

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;
}
@NonNull

會在 setter 產生 null check,發生 null 時,會產生 NullPointerException。

@Getter
@Setter
@NonNull
private String lastname;

等同以下 java code

public void setLastname(String lastname) {
    if (lastname == null) throw new java.lang.NullPointerException("lastname");
    this.lastname = lastname;
}

public String getLastname() {
    return lastname;
}
@ToString
@ToString(callSuper=true,exclude="employed")
public class TestBean {
    private boolean employed = true;
    private String name;
    private String lastname;
}

等同以下 java code

public class TestBean {
   private boolean employed = true;
    private String name;
    private String lastname;
    
    @java.lang.Override
    public java.lang.String toString() {
        return "TestBean("super=" + super.toString() +
            ", name =" + name +
            ", lastname =" + lastname + ")";
    }
}
@EqualsAndHashCode

產生 equals 與 hashCode

@EqualsAndHashCode(callSuper=true, exclude={"employed"})
public class TestBean extends Person {
    private boolean employed = true;
    private String name;
    private String lastname;
}

等同以下 java code

public class TestBean extends Person {
    private boolean employed = true;
    private String name;
    private String lastname;
    
    @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 TestBean other = (TestBean)o;
        if (this.name == null ? other.name != null : !this.name.equals(other.name)) return false;
        if (this.lastname == null ? other.lastname != null : !this.lastname.equals(other.lastname)) 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.lastname == null ? 0 : this.lastname.hashCode());
        return result;
    }
}
@Data

這是 Lombok 最常用的 annotation,包含了 @ToString, @EqualsAndHashCode, @Getter, @Setter 的功能。

@Cleanup

可確保 resource 會被 released,但 Java 7 以後已經有了 auto resource management 的功能,所以可以不使用這個功能。

@Synchronized

會產生一個 locking field,也就是 $lock 物件,用來同步該 method

private DateFormat format = new SimpleDateFormat("MM-dd-YYYY");

@Synchronized
public String synchronizedFormat(Date date) {
    return format.format(date);
}

等同以下 java code

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);
    }
}
@NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor

@NoArgsConstructor 會產生沒有 parameters 的 constructor,但如果有 final field 時,則無法使用這個 annotation,會發生編譯錯誤。

但可以改用 @NoArgsConstructor(force = true),這會將所有 final fields 初始化為 0/false/null。

@RequiredArgsConstructor 會產生一個 constructor,針對每一個需要特殊處理的 fields,都會產生一個參數,例如 final field,以及標記為 @NonNull field。

@AllArgsConstructor 會產生一個 constructor,class 裡面每一個 field 都產生一個對應的 constructor 參數

javap

編譯後的 class 可利用 javap 工具反組譯,並瞭解 Lombok 產生的 class 裡面包含的內容。

$ javap TestBean.class
Compiled from "TestBean.java"
public class test.TestBean extends test.Person {
  public test.TestBean();
  public boolean equals(java.lang.Object);
  protected boolean canEqual(java.lang.Object);
  public int hashCode();
}

Summary

其他的 annotaion 可參考 features 網頁的說明,但明顯最常用的是 @Data,這個功能就能減輕不少工作。

References

Reducing Boilerplate Code with Project Lombok

沒有留言:

張貼留言