@Builder
was introduced as experimental feature in lombok v0.12.0.
@Builder
gained @Singular
support and was promoted to the main lombok
package since lombok v1.16.0.
The @Builder
annotation produces complex builder APIs for your classes.
@Builder
lets you automatically produce the code required to have your class be instantiable with code such as:
Person.builder().name("Adam Savage").city("San Francisco").job("Mythbusters").job("Unchained Reaction").build();
@Builder
can be placed on a class, or on a constructor, or on a static method. While the "on a class" and "on a constructor"
mode are the most common use-case, @Builder
is most easily explained with the "static method" use-case.
A static method annotated with @Builder
(from now on called the target) causes the following 7 things to be generated:
FooBuilder
, with the same type arguments as the static method (called the builder).build()
method which calls the static method, passing in each field. It returns the same type that the
target returns.toString()
implementation.builder()
method, which creates a new instance of the builder.@EqualsAndHashCode
on the builder class.
@Builder
can generate so-called 'singular' methods for collection parameters/fields. These take 1 element instead of an entire list, and add the
element to the list. For example: Person.builder().job("Mythbusters").job("Unchained Reaction").build();
would result in the List<String> jobs
field to have 2 strings in it. To get this behaviour, the field/parameter needs to be annotated with @Singular
. The feature has its own documentation.
Now that the "static method" mode is clear, putting a @Builder
annotation on a constructor functions similarly; effectively,
constructors are just static methods that have a special syntax to invoke them: Their 'return type' is the class they construct, and their
type parameters are the same as the type parameters of the class itself.
Finally, applying @Builder
to a class is as if you added @AllArgsConstructor(access = AccessLevel.PACKAGE)
to the class and applied the
@Builder
annotation to this all-args-constructor. This only works if you haven't written any explicit constructors yourself. If you do have an
explicit constructor, put the @Builder
annotation on the constructor instead of on the class.
If using @Builder
to generate builders to produce instances of your own class (this is always the case unless adding @Builder
to a static method that doesn't return your own type), you can use @Builder(toBuilder = true)
to also generate an instance method in your class called toBuilder()
; it creates a new builder that starts out with all the values of this instance. You can put the @Builder.ObtainVia
annotation on the parameters (in case of a constructor or static method) or fields (in case of @Builder
on a type) to indicate alternative means by which the value for that field/parameter is obtained from the instance. For example, you can specify a method to be invoked: @Builder.ObtainVia(method = "calculateFoo")
.
The name of the builder class is FoobarBuilder
, where Foobar is the simplified, title-cased form of the return type of the
target - that is, the name of your type for @Builder
on constructors and types, and the name of the return type for @Builder
on static methods. For example, if @Builder
is applied to a class named com.yoyodyne.FancyList<T>
, then the builder name will be
FancyListBuilder<T>
. If @Builder
is applied to a static method that returns void
, the builder will be named
VoidBuilder
.
The configurable aspects of builder are:
"build"
)"builder"
)toBuilder()
(default: no)@Builder(builderClassName = "HelloWorldBuilder", buildMethodName = "execute", builderMethodName = "helloWorld", toBuilder = true)
By annotating one of the parameters (if annotating a static method or constructor with @Builder
) or fields (if annotating a class with @Builder
) with the
@Singular
annotation, lombok will treat that builder node as a collection, and it generates 2 'adder' methods instead of a 'setter' method. One which adds a single element to the collection, and one
which adds all elements of another collection to the collection. No setter to just set the collection (replacing whatever was already added) will be generated. These 'singular' builders
are very complicated in order to guarantee the following properties:
build()
, the produced collection will be immutable.build()
does not modify any already generated objects, and, if build()
is later called again, another collection with all the elements added since the creation of the builder is generated.
@Singular
can only be applied to collection types known to lombok. Currently, the supported types are:
java.util
:Iterable
, Collection
, and List
(backed by a compacted unmodifiable ArrayList
in the general case).Set
, SortedSet
, and NavigableSet
(backed by a smartly sized unmodifiable HashSet
or TreeSet
in the general case).Map
, SortedMap
, and NavigableMap
(backed by a smartly sized unmodifiable HashMap
or TreeMap
in the general case).com.google.common.collect
:ImmutableCollection
and ImmutableList
(backed by the builder feature of ImmutableList
).ImmutableSet
and ImmutableSortedSet
(backed by the builder feature of those types).ImmutableMap
, ImmutableBiMap
, and ImmutableSortedMap
(backed by the builder feature of those types).
If your identifiers are written in common english, lombok assumes that the name of any collection with @Singular
on it is an english plural and will attempt to automatically
singularize that name. If this is possible, the add-one method will use this name. For example, if your collection is called statuses
, then the add-one method will automatically
be called status
. You can also specify the singular form of your identifier explictly by passing the singular form as argument to the annotation like so: @Singular("axis") List<Line> axes;
.
If lombok cannot singularize your identifier, or it is ambiguous, lombok will generate an error and force you to explicitly specify the singular name.
The snippet below does not show what lombok generates for a @Singular
field/parameter because it is rather complicated.
You can view a snippet here.
lombok.builder.flagUsage
= [warning
| error
] (default: not set)@Builder
as a warning or error if configured.lombok.singular.useGuava
= [true
| false
] (default: false)true
, lombok will use guava's ImmutableXxx
builders and types to implement java.util
collection interfaces, instead of creating
implementations based on Collections.unmodifiableXxx
. You must ensure that guava is actually available on the classpath and buildpath if you use this setting.
Guava is used automatically if your field/parameter has one of the guava ImmutableXxx
types.
lombok.singular.auto
= [true
| false
] (default: true)true
(which is the default), lombok automatically tries to singularize your identifier name by assuming that it is a common english plural.
If false
, you must always explicitly specify the singular name, and lombok will generate an error if you don't (useful if you write your code in a language other than english).
@Singular support for java.util.NavigableMap/Set
only works if you are compiling with JDK1.8 or higher.
You cannot manually provide some or all parts of a @Singular
node; the code lombok generates is too complex for this. If you want to
manually control (part of) the builder code associated with some field or parameter, don't use @Singular
and add everything you need manually.
The sorted collections (java.util: SortedSet
, NavigableSet
, SortedMap
, NavigableMap
and guava: ImmutableSortedSet
, ImmutableSortedMap
) require that the type argument of the collection has natural order (implements java.util.Comparable
). There is no way to pass an explicit Comparator
to use in the builder.
An ArrayList
is used to store added elements as call methods of a @Singular
marked field, if the target collection is from the java.util
package, even if the collection is a set or map. Because lombok ensures that generated collections are compacted, a new backing instance of a set or map must be constructed anyway, and storing the data as an ArrayList
during the build process is more efficient that storing it as a map or set. This behaviour is not externally visible, an an implementation detail of the current implementation of the java.util
recipes for @Singular @Builder
.
With toBuilder = true
applied to static methods, any type parameter on the annotated static method must show up in the returntype.