來源:生活點亮技術
1.概覽
在這個教程中,我們将研究如何基于Lombok在實作
Builder
模式時為屬性提供預設值。
請務必閱讀這篇Lombok簡介 。
2.Maven依賴
在本教程中,我們将使用Lombok ,是以,需要添加一個Maven依賴:
org.projectlombok
lombok
1.16.18
provided
3.基于 Lombok
Builder
的POJO
Lombok
Builder
首先,讓我們看看
Lombok
如何幫助我們從實作
Builder
模式所需的樣闆代碼中解脫出來。 我們将從一個簡單的
POJO
開始:
public class Pojo {
private String name;
private boolean original;
}
為了使這個類可用,我們需要給每個字段實作一個
getter
。另外,如果希望将這個類用于
ORM
,我們可能需要一個預設構造函數。
除了這些,我們還需要一個用于建構這個
POJO
的
Builder
類。有了
Lombok
,我們就可以通過一些簡單的注解來實作上面這樣功能了:
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Pojo {
private String name;
private boolean original;
}
4.期望的效果
讓我們以單元測試的形式來定義想要達到的效果。
最重要,也是最基本的要求是使用
builder
方法建構對象之後要有預設值:
@Test
public void givenBuilderWithDefaultValue_ThenDefaultValueIsPresent() {
Pojo build = Pojo.builder()
.build();
Assert.assertEquals("foo", build.getName());
Assert.assertTrue(build.isOriginal());
}
當然,這個測試用例肯定會失敗,因為
@Builder
注解并不會給屬性賦預設值。 稍後,我們會讓測試通過。 如果使用了依賴預設構造函數的
ORM
架構,那麼我們應該先從預設構造函數開始:
@Test
public void givenBuilderWithDefaultValue_NoArgsWorksAlso() {
Pojo build = Pojo.builder()
.build();
Pojo pojo = new Pojo();
Assert.assertEquals(build.getName(), pojo.getName());
Assert.assertTrue(build.isOriginal() == pojo.isOriginal());
}
在這個階段,這個測試用例通過了,因為都沒有預設值。 現在,讓我們看看如何讓這兩個測試用例都通過!
5. Lombok
的 Builder.Default
注解
Lombok
Builder.Default
自從
Lombok
v1.16.16之後,我們就可以使用
@Builder.Default
注解:
// 添加在類上的注解同上
public class Pojo {
@Builder.Default
private String name = "foo";
@Builder.Default
private boolean original = true;
}
這個注解簡單易讀,但也有一些缺陷。
有了這個注解,預設值将将與構造函數一起出現,那麼第一個測試用例将通過。不幸的是,由于
@NoArgsConstructor
不會得到預設值,是以第二個測試用例失敗了。即使無參構造函數不是
Lombok
自動生成而是顯式編寫的,也取不到預設值。
Builder.Default
注解的這種副作用從一開始就有,可能還會持續很長時間。
6.初始化 Builder
Builder
為了讓這兩個測試用例通過,我們可以嘗試通過在一個極簡的
Builder
中定義預設值:
// 添加在類上的注解同上
public class Pojo {
private String name = "foo";
private boolean original = true;
public static class PojoBuilder {
private String name = "foo";
private boolean original = true;
}
}
通過這種方式,這兩個測試用例都通過了。 很不幸,代價是代碼重複。對于具有數十個屬性的POJO,維護雙重初始化可能會出錯。
但是,如果願意付出這個代價,我們還應該注意一件事。如果我們使用IDE中的重構功能來重命名類,靜态内部類将不會自動重命名。這樣,
Lombok
就找不到它了,我們的代碼就出錯了。
為了消除這種風險,我們可以再配置一下
Builder
注解:
// 添加在類上的注解同上
@Builder(builderClassName = "PojoBuilder")
public class Pojo {
private String name = "foo";
private boolean original = true;
public static class PojoBuilder {
private String name = "foo";
private boolean original = true;
}
}
7.使用 toBuilder
參數
toBuilder
@Builder
還支援在原始類的執行個體中生成一個
Builder
執行個體。預設情況下這個特性是關閉的。我們可以通過在
Builder
注解中配置
toBuilder
參數來啟用:
// class annotations as before
// 添加在類上的注解同上
@Builder(toBuilder = true)
public class Pojo {
private String name = "foo";
private boolean original = true;
}
這樣,我們就可以避免*雙重初始化 *。
當然,這也是有代價的。我們必須通過 執行個體化這個類來建立一個
Builder
執行個體。是以,相關的測試用例也必須修改:
@Test
public void givenBuilderWithDefaultValue_ThenDefaultValueIsPresent() {
Pojo build = new Pojo().toBuilder()
.build();
Assert.assertEquals("foo", build.getName());
Assert.assertTrue(build.isOriginal());
}
@Test
public void givenBuilderWithDefaultValue_thenNoArgsWorksAlso() {
Pojo build = new Pojo().toBuilder()
.build();
Pojo pojo = new Pojo();
Assert.assertEquals(build.getName(), pojo.getName());
Assert.assertTrue(build.isOriginal() == pojo.isOriginal());
}
同樣,這兩個測試用例會全部通過。是以使用無參構造函數與使用
Builder
具有相同的預設值。
8.總結
至此,我們已經展示了為
Lombok
Builder
提供預設值的幾種方法。
Builder.Default
注解的副作用也很明顯。但是,其他幾個方案也有缺點。是以我們必須結合自身情況謹慎選擇。
照例,文中用到的代碼都可以在 GitHub上找到。
原文連結:https://www.baeldung.com/lombok-builder-default-value
作者: baeldung
譯者: helloworldtang
·END·
近期熱文:
-
Spring Cloud Stream 學習小清單
-
Spring Cloud Stream 使用延遲消息實作定時任務(RabbitMQ)
-
了解Java中的記憶體洩漏
-
Git 常用指令清單,掌握這些,輕松駕馭版本管理
-
優先級隊列(頭條面試題)
-
來談下高并發和分布式中的幂等處理
-
你應該知道的7個寫出更好的 Java 代碼的技巧
-
百億資料量下,掌握這些Redis技巧你就能Hold全場
-
深入聊一聊 Spring AOP 實作機制
-
我說分布式事務之最大努力通知型事務
-
我說分布式事務之TCC
-
不可錯過的CMS學習筆記
-
可能是最全面的G1學習筆記
看完,趕緊點個“好看”鴨
點鴨點鴨
↓↓↓↓