@Builder
......鮑勃是你的叔叔:用於創建對象的無懈可擊的花式褲子!
@Builder
在lombok v0.12.0中作為實驗特征介紹。
@Builder
獲得了@Singular
支持,並lombok從lombok v1.16.0 升級到主程序包。
@Builder
與@Singular
增加,因為龍目島v1.16.8一個明確的方法。
@Builder.Default
功能已在lombok v1.16.16中添加。
Overview
該@Builder
標注生產絡合劑的API為你的類。
@Builder
允許您使用以下代碼自動生成使您的類可實例化所需的代碼:
Person.builder().name("Adam Savage").city("San Francisco").job("Mythbusters").job("Unchained Reaction").build();
@Builder
可以放在類,構造函數或方法上。雖然“在類上”和“在構造函數上”模式是最常見的用例,但@Builder
最容易用“方法”用例來解釋。
用@Builder
(從現在開始調用目標)注釋的方法會導致生成以下7件事:
- 一個名為的內部靜態類
*Foo*Builder
,具有與靜態方法相同的類型參數(稱為構建器)。 - 在構建器中:目標的每個參數的一個私有非靜態非最終字段。
- 在構建器中:一個包私有no-args空構造函數。
- 在構建器中:對於目標的每個參數,類似於“setter”的方法:它具有與該參數相同的類型和相同的名稱。它返回構建器本身,以便可以鏈接setter調用,如上例所示。
- 在構建器中:
build()
調用方法的方法,傳入每個字段。它返回與目標返回的相同類型。 - 在構建器中:一個明智的
toString()
實現。 - 在包含目標的類中:一種
builder()
方法,它創建構建器的新實例。
如果該元素已經存在,則將以靜默方式跳過每個列出的生成元素(忽略參數計數並僅查看名稱)。這包括構建器本身:如果該類已經存在,則lombok將簡單地開始在此已存在的類中注入字段和方法,除非當然要存在要注入的字段/方法。您可能不會在構建器類上放置任何其他方法(或構造函數)生成lombok注釋; 例如,您不能放置@EqualsAndHashCode
構建器類。
@Builder
可以為集合參數/字段生成所謂的“奇異”方法。它們采用1個元素而不是整個列表,並將該元素添加到列表中。例如:Person.builder().job("Mythbusters").job("Unchained Reaction").build();
將導致該List<String> jobs
字段中包含2個字符串。要獲得此行為,需要使用注釋字段/參數@Singular
。該功能有自己的文檔。
既然“方法”模式已經清楚了,那么@Builder
在構造函數上添加注釋的功能類似; 實際上,構造函數只是具有特殊語法來調用它們的靜態方法:它們的“返回類型”是它們構造的類,它們的類型參數與類本身的類型參數相同。
最后,應用於@Builder
類就好像您已添加@AllArgsConstructor(access = AccessLevel.PACKAGE)
到類中並將@Builder
注釋應用於此all-args構造函數。這僅適用於您自己沒有編寫任何顯式構造函數的情況。如果您確實有一個顯式構造函數,請將@Builder
注釋放在構造函數而不是類上。
如果使用@Builder
生成構建器來生成自己的類的實例(除非添加@Builder
到不返回自己類型的方法,否則總是如此),您可以使用@Builder(toBuilder = true)
在類中生成實例方法調用toBuilder()
; 它會創建一個以該實例的所有值開頭的新構建器。您可以將@Builder.ObtainVia
注釋放在參數(如果是構造函數或方法)或字段(如果@Builder
是類型)上,以指示從該實例獲取該字段/參數的值的替代方法。例如,您可以指定要調用的方法:@Builder.ObtainVia(method = "calculateFoo")
。
構建器類的名稱是*Foobar*Builder
,其中Foobar是目標返回類型的簡化,標題框形式- 即@Builder
構造函數和類型的類型名稱,以及@Builder
on方法的返回類型的名稱。例如,如果@Builder
應用於名為的類com.yoyodyne.FancyList<T>
,則構建器名稱將為FancyListBuilder<T>
。如果@Builder
應用於返回的方法,void
則將命名構建器VoidBuilder
。
構建器的可配置方面是:
- 該生成器的類名(默認:返回類型+“生成器”)
- 該版本()方法的名稱(默認:
"build"
) - 該生成器()方法的名稱(默認:
"builder"
) - 如果你想
toBuilder()
(默認:否)
所有選項均從其默認值更改的示例用法:
@Builder(builderClassName = "HelloWorldBuilder", buildMethodName = "execute", builderMethodName = "helloWorld", toBuilder = true)
@Builder.Default
如果在構建會話期間從未設置某個字段/參數,則它始終為0 / null/ false。如果您已經放置@Builder了一個類(而不是方法或構造函數),則可以直接在該字段上指定默認值,並使用以下內容對該字段進行注釋@Builder.Default
:
@Builder.Default private final long created = System.currentTimeMillis();
@Singular
通過使用注釋注釋其中一個參數(如果使用方法或構造函數進行注釋@Builder
)或字段(如果使用注釋類@Builder
)@Singular
,lombok將該構建器節點視為集合,並生成2個“加法器”方法而不是“ setter'方法。一個向集合添加單個元素,另一個將另一個集合的所有元素添加到集合中。將不生成僅設置集合(替換已添加的任何內容)的setter。還生成了“清晰”方法。這些“單一”構建器非常復雜,以保證以下屬性:
- 調用時
build()
,生成的集合將是不可變的。 - 在調用之后調用“adder”方法之一或“clear”方法
build()
不會修改任何已生成的對象,並且如果build()
稍后再次調用,則會生成自生成構建器以來添加了所有元素的另一個集合。 - 生成的集合將被壓縮到最小的可行格式,同時保持高效。
@Singular
只能應用於lombok已知的集合類型。目前,支持的類型是:
-
java.util
:-
Iterable
,Collection
和List
(ArrayList
在一般情況下由壓縮的不可修改的支持)。 -
Set
,SortedSet
和NavigableSet
(由一個聰明的大小不可修改HashSet
或TreeSet
在一般情況下支持)。 -
Map
,SortedMap
和NavigableMap
(由一個聰明的大小不可修改HashMap
或TreeMap
在一般情況下支持)。
-
- 番石榴的
com.google.common.collect
:-
ImmutableCollection
和ImmutableList
(由構建器功能支持ImmutableList
)。 -
ImmutableSet
和ImmutableSortedSet
(由這些類型的構建器功能支持)。 -
ImmutableMap
,ImmutableBiMap
和ImmutableSortedMap
(由這些類型的構建器功能支持)。 -
ImmutableTable
(由構建器功能支持ImmutableTable
)。
-
如果您的標識符是用通用英語編寫的,則lombok假定其上的任何集合的名稱@Singular
是英語復數,並將嘗試自動單獨化該名稱。如果可以,add-one方法將使用此名稱。例如,如果調用了您的集合statuses
,則會自動調用add-one方法status
。您還可以通過將單數形式作為參數傳遞給注釋來明確指定標識符的單數形式,如下所示:@Singular("axis") List<Line> axes;
。
如果lombok無法單獨標識您的標識符,或者它不明確,則lombok將生成錯誤並強制您明確指定單數名稱。
下面的代碼段沒有顯示lombok為@Singular
字段/參數生成的內容,因為它相當復雜。您可以在此處查看代碼段。
With Jackson
您可以自定義構建器類的自定義部分,例如向構建器類添加另一個方法,或者在構建器類中注釋方法。Lombok將生成您不手動添加的所有內容,並將其放入此構建器類中。例如,如果您嘗試將jackson配置為對集合使用特定子類型,則可以編寫如下內容:
@Value @Builder
@JsonDeserialize(builder = JacksonExample.JacksonExampleBuilder.class)
public class JacksonExample { @Singular private List<Foo> foos; @JsonPOJOBuilder(withPrefix = "") public static class JacksonExampleBuilder implements JacksonExampleBuilderMeta { } private interface JacksonExampleBuilderMeta { @JsonDeserialize(contentAs = FooImpl.class) JacksonExampleBuilder foos(List<? extends Foo> foos) } }
With Lombok
import lombok.Builder; import lombok.Singular; import java.util.Set; @Builder public class BuilderExample { @Builder.Default private long created = System.currentTimeMillis(); private String name; private int age; @Singular private Set<String> occupations; }
Vanilla Java
import java.util.Set; public class BuilderExample { private long created; private String name; private int age; private Set<String> occupations; BuilderExample(String name, int age, Set<String> occupations) { this.name = name; this.age = age; this.occupations = occupations; } private static long $default$created() { return System.currentTimeMillis(); } public static BuilderExampleBuilder builder() { return new BuilderExampleBuilder(); } public static class BuilderExampleBuilder { private long created; private boolean created$set; private String name; private int age; private java.util.ArrayList<String> occupations; BuilderExampleBuilder() { } public BuilderExampleBuilder created(long created) { this.created = created; this.created$set = true; return this; } public BuilderExampleBuilder name(String name) { this.name = name; return this; } public BuilderExampleBuilder age(int age) { this.age = age; return this; } public BuilderExampleBuilder occupation(String occupation) { if (this.occupations == null) { this.occupations = new java.util.ArrayList<String>(); } this.occupations.add(occupation); return this; } public BuilderExampleBuilder occupations(Collection<? extends String> occupations) { if (this.occupations == null) { this.occupations = new java.util.ArrayList<String>(); } this.occupations.addAll