@Builder

Since

@Builder was introduced as experimental feature in lombok v0.12.0.

Experimental

Experimental because:

  • New feature - community feedback requested.
Current status: sure thing - This feature will move to the core package soon.

Overview

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").worksAt("Mythbusters").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:

  • An inner static class named FooBuilder, with the same type arguments as the static method (called the builder).
  • In the builder: One private non-static non-final field for each parameter of the target.
  • In the builder: A package private no-args empty constructor.
  • In the builder: A 'setter'-like method for each parameter of the target: It has the same type as that parameter and the same name. It returns the builder itself, so that the setter calls can be chained, as in the above example.
  • In the builder: A build() method which calls the static method, passing in each field. It returns the same type that the target returns.
  • In the builder: A sensible toString() implementation.
  • In the class containing the target: A builder() method, which creates a new instance of the builder.
Each listed generated element will be silently skipped if that element already exists (disregarding parameter counts and looking only at names). This includes the builder itself: If that class already exists, lombok will simply start injecting fields and methods inside this already existing class, unless of course the fields / methods to be injected already exist. You may not put any other method (or constructor) generating lombok annotation on a builder class though; for example, you can not put @EqualsAndHashCode on the builder class.

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.

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:

  • The builder's class name (default: return type + 'Builder')
  • The build() method's name (default: "build")
  • The builder() method's name (default: "builder")
  • The 'fluent' nature of the generated 'setter'-like methods. A fluent 'setter' is named just fieldName(), a non-fluent one is named setFieldName(). (default: true)
  • The 'chain' nature of the generated 'setter'-like methods. A chainable 'setter' returns the builder object itself, letting you chain 'set' calls. A non-chainable setter returns void. (default: true)
Example usage where all options are changed from their defaults:
@Builder(builderClassName = "HelloWorldBuilder", buildMethodName = "execute", builderMethodName = "helloWorld", fluent = false, chain = false)

With Lombok

1 import lombok.experimental.Builder;
2 
3 @Builder
4 public class BuilderExample {
5   private String name;
6   private int age;
7 }

Vanilla Java

01 public class BuilderExample {
02   private String name;
03   private int age;
04   
05   BuilderExample(String name, int age) {
06     this.name = name;
07     this.age = age;
08   }
09   
10   public static BuilderExampleBuilder builder() {
11     return new BuilderExampleBuilder();
12   }
13   
14   public static class BuilderExampleBuilder {
15     private String name;
16     private int age;
17     
18     BuilderExampleBuilder() {
19     }
20     
21     public BuilderExampleBuilder name(String name) {
22       this.name = name;
23       return this;
24     }
25     
26     public BuilderExampleBuilder age(int age) {
27       this.age = age;
28       return this;
29     }
30     
31     public BuilderExample build() {
32       return new BuilderExample(name, age);
33     }
34     
35     @java.lang.Override
36     public String toString() {
37       return "BuilderExample.BuilderExampleBuilder(name = " this.name + ", age = " this.age + ")";
38     }
39   }
40 }

Supported configuration keys:

lombok.builder.flagUsage = [warning | error] (default: not set)
Lombok will flag any usage of @Builder as a warning or error if configured.

Small print

Another strategy for fluent APIs is that the programmer using your library statically imports your 'builder' method. In this case, you might want to name your builder method equal to your type's name. So, the builder method for a class called Person would become person(). This is nicer if the builder method is statically imported.

If the return type of your target static method is a type parameter (such as T), lombok will enforce an explicit builder class name.

You don't HAVE to use @Builder to build anything; you can for example put it on a static method that has lots of parameter to improve the API of it. In this case, we suggest you use buildMethodName = to rename the build method to execute() instead.

The builder class will NOT get an auto-generated implementation of hashCode or equals methods! These would suggest that it is sensible to use instances of a builder as keys in a set or map. However, that's not a sensible thing to do. Hence, no hashCode or equals.

Generics are sorted out for you.

If an explicit constructor is present, but @Builder is placed on the class, then the builder will be generated as if an explicit constructor is present with the same arguments list as what @AllArgsConstructor would produce. If this constructor does not exist, a compile time error will result. Usually you should just either let lombok make this constructor (delete your constructor from the source), or, move the @Builder annotation to the constructor.