今天讓我們探討一下如何利用 Lombok 工具來提升 Java 開發中的效率。提到 Java,很多人的第一反應就是臃腫,這主要是因為在日常程式設計中,我們不得不編寫許多重複的程式碼,例如對類的屬性寫的 Setter、Getter 方法,下面就是一個簡單的例子:
public class Student { private int age; private String name; }
在上面的程式碼中,我們定義了一個名為 Student 的類,併爲其新增了兩個屬性:age 和 name。如果我們要在其他地方使用這個類,還需要為這個類宣告訪問方法,也就是 Getter 和 Setter 方法。現在一些主流的 IDE,比如 IntelliJ IDEA,提供了可以自動生成 Getter 和 Setter 函式的方法。
下圖是在 IDEA 中,單擊右鍵時生成 Getter/Setter 方法的截圖(我們可以看到 IDEA 還支援生成其他常用的方法,比如 equals()、hashCode()、toString() 等等。
在使用 IDEA 自動填充了 Getter 和 Setter 方法之後,Student 類的程式碼感覺瞬間臃腫了很多,具體程式碼如下:
public class Student { private int age; private String name; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
在我們建立一個類之後,除了一般會用到的 Getter 和 Setter 方法之外,我們有時候可能還需要用到構造方法和 toString 方法,把這兩個方法補充完整之後 student 類的程式碼如下所示:
public class Student { private int age; private String name; public Student() { } public Student(int age, String name) { this.age = age; this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Student{" + "age=" + age + ", name='" + name + '\'' + '}'; } }
看完之後,不得不說這樣自動補全出來的程式碼實在是太臃腫了,其實我們關心就是前面兩行程式碼,知道有哪些成員,剩下的 Getter/Setter/toString 不看也可以。不管對於寫程式碼的同學,還是看程式碼的同學,這些冗餘程式碼都是一種負擔。
那麼我們不禁要問,對於這些固定模式的程式碼有沒有一種精簡的處理方式呢?答案是肯定的,對於這個問題,有一種比較主流的解決方案——就是 Lombok。
Lombok 是什麼
在 Lombok 的官網上,對於 Lombok 的介紹如下:
Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java. Never write another getter or equals method again, with one annotation your class has a fully featured builder, Automate your logging variables, and much more.
簡單來說,就像上面這段介紹提到的,Lombok 是一個 Java 庫,也可以看作是一個開發工具外掛。我們可以透過註解的方式引入 Lombok,在編譯期間,它會自動為我們生成一些固定模式的程式碼,比如 Getter/Setter 等。
引入 Lombok ,可以讓我們的程式碼變得更加簡潔,也能夠讓我們更專注於主要的邏輯上,同時也能減少我們編寫程式碼時可能會出現的 bug。
如何使用 Lombok
還是以上面的程式碼為例,看看如何使用 Lombok 來簡化這些冗餘的程式碼呢?首先,我們要在專案中引入 Lombok 的依賴。不過要注意的是,要是使用 IDE 進行開發,那麼還需要安裝一下對應的 Lombok 外掛。
下面是對引入 Lombok 依賴的程式碼演示,這裏的程式碼是基於 JDK 8 進行的,當然 JDK 11 也是類似的。
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.20</version> </dependency>
Getter/Setter
既然每一個類中最常見的冗餘程式碼就是 getter 方法和 setter 方法,那麼我們就來先看一下如何使用 Lombok 來處理這兩個常見的冗餘方法。簡單來說,我們透過註解 @Getter/@Setter 修飾 Class 的成員的這種做法,等價於為其生成 Getter 和 Setter 方法這種做法。
下面的程式碼所展示的,是使用註解 @Getter/@Setter 來避免 Getter 和 Setter 方法冗餘的具體操作。
import lombok.Getter; import lombok.Setter; public class Student { @Getter @Setter private int age; @Getter @Setter private String name; }
當然,這時候我們還可以透過其他註解來生成其他需要的方法:
@ToString 為其生成 toString() 方法;
@EqualsAndHashCode 生成 equals() 和 hashCode() 方法。
但是,如果需要這麼多功能方法的話,我們一般選擇註解 @Data。
@Data
之所以註解 @Data 能夠應用於需要多種功能的方法,則因為註解 @Data 實際上是多個註解組成的一個註解集合。註解 @Data 等價於如下註解的集合:
@Getter 會被新增到所有成員變數;
@Setter 新增到所有非 final 的成員變數(final 成員不能使用者 Setter 方法);
@ToString;
@EqualsAndHashCode;
@RequiredArgsConstructor 生成以特定引數(包括 final 和特定引數如 @NonNull 修飾的成員變數)為入參的建構函式。
下面我們用註解 @RequiredArgsConstructor 來舉個例子:
@RequiredArgsConstructor @ToString public class Student { private int age; private final int number; @NotNull private String name; public static void main(String[] args) { Student student = new Student(1, "xiaoming"); System.out.println(student); } }
在上面的程式碼片段中,註解 @RequiredArgsConstructor 等價於如下的建構函式:
public class Student { public Student(int number, String name) { this.number = number; this.name = name; } }
所以,上面使用的註解 @RequiredArgsConstructor 的程式碼的輸出應該為:
Student(age=0, number=1, name=xiaoming)
NoArgsConstructor/AllArgsConstructor
針對建構函式,除了註解 @RequiredArgsConstructor,Lombok 還提供了另外兩個選擇:
@NoArgsConstructor 不接受引數的建構函式,這種情況下要求 Class 內沒有 final 修飾的成員變數
@AllArgsConstructor 每個成員變數都要接受一個引數的建構函式
為方便起見,我們在一個 Student 類中同時新增 @NoArgsConstructor 和 @AllArgsConstructor 兩個註解(建構函式過載)做展示,下面是具體的程式碼示例:
@NoArgsConstructor @AllArgsConstructor public class Student { private int age; private String name; }
上面的程式碼等價於:
public class Student { private int age; private String name; public Student() {} public Student(int age, String name) { this.age = age; this.name = name; } }
log
除了上面我們講到的對諸多方法的支援,Lombok 還提供對 log 的支援,也就是為日誌的輸出和列印提供了便利。在不使用 Lombok 的情況下,如果要列印 log,那麼就需要自己初始化一個 org.slf4j.Logger
的例項。
初始化 org.slf4j.Logger
的具體程式碼如下:
public class Student { private static final org.slf4j.Logger LOGGER = org.slf4j.LoggerFactory.getLogger(Student.class); public void foo() { LOGGER.info(...); } }
同上麵初始化 org.slf4j.Logger
的程式碼相比,使用 Lombok 輸出和列印日誌就會簡單很多,具體程式碼如下:
@Slf4j public class Student { public void foo() { log.info(...); } }
Builder
藉助於註解 @Builder 我們可以以 Fluent 的風格進行程式設計。Fluent 風格是一種鏈式的程式設計方式,比如在 curator API 中(Curator 是 Netflix 公司開源的一套 Zookeeper 客戶端框架,解決了很多 Zookeeper 客戶端非常底層的細節開發工作,包括連線重連、反覆註冊 Watcher 和 NodeExistsException 異常等等),就大量使用 Fluent 的程式設計風格(如下)。這種方式在構造物件引數不定的情況下會比較好用。
下面的程式碼構造了一個 CuratorFramework 的 Client,這裏就採用了 Fluent 程式設計風格。
CuratorFramework client = CuratorFrameworkFactory.builder() .connectString("domain.book.zookeeper:2181") .sessionTimeoutMs(5000) .retryPolicy(retryPolicy) .build()
透過註解 @Builder 使用 Fluent 風格也比較簡單,直接在 Class 上面新增註解即可,具體的程式碼示例如下:
@Builder public class Student { private int age; private int number; private String name; public static void main(String[] args) { Student student = Student.builder() .age(18) .name("Jack") .number(1000) .build(); } }
從上面的程式碼中,我們能看到這種鏈式的程式設計方式優勢在於構造物件的時候可以初始化任意成員,而不需要定義建構函式,這為我們程式碼帶來了極大的簡潔性。但註解 @Builder 有一個缺點:在子類中無法透過 builder
方法構造父類中的成員變數。對於這種情況我們可以使用 @SuperBuilder 來替換。
至於 Lombok 剩下的註解這裏就不再詳述了,你可以參考: projectlombok.org/features/al…
Lombok 的缺點
最後我們再說說 Lombok 的缺點,畢竟凡事都是有兩面性的,主要有以下兩點:
需要 IDE 支援 “annotation processing”,也就是 IDE 要支援對註解的處理,目前的主流 Java IDE 都已經支援了,比如 IntelliJ IDEA、NetBeans、Eclipse。
和編譯器強繫結。Lombok 會在編譯期間呼叫編譯器的 API 生成中間程式碼。這個過程可能會呼叫 non-public API 進而導致相容性問題:也就是說,一個使用 Lombok 的專案,在 JDK 7 下是正常執行的,但是升級到 JDK 8 之後不行了。這個時候我們往往要順帶升級一下 Lombok 的版本。
總結
今天分享的內容比較簡單,就是在日常開發中如何透過引入 Lombok 來精簡我們的程式碼,進而極大提高開發效率。在下面,我對 Lombok 中的不同註解用腦圖的方式做了總結。