Searching for a plugin to avoid boilerplate code to implement Joshua Bloch's builder pattern I found the amazing Lombok Project which enables you to generate builders via annotations like this:
@Builder
public class Person {
private String name;
private String address;
private String secondAddress;
}
PersonBuilder.builder().name("yourName").address("your Address").build();
As you can see, there is no boilerplate code, and you can easily create an instance of Person
by calling the provided static builder()
method, chaining setter-like-calls just like it works with JavaBeans-Pattern and end the chain with calling build()
;
One of the disadvantages of the JavaBeans-Pattern compared to builder pattern is (from Effective Java):
Because construction is split across multiple calls, a JavaBean may be in an inconsistent state partway through its construction.
Assuming that in the above example, the first two attributes, name and address, are mandatory for constructing an instance of Person, the way Lombok implements the builder pattern enables a developer to split/shorten the construction and do something with a possibly inconsistent instance of Person
, like this:
Person p = PersonBuilder.builder().name("yourName").build();
...
System.out.println(p.getAddress());
...
p.setAddress("your address");
Joshua Bloch's solution prefers a builder method with the mandatory attributes as parameters, so that there is no possibility that the construction is split across multiple calls, like illustrated in Item 2: Consider a builder when faced with many constructor parameters.
My question is: Is there any convenient way like annotation parameters for @Builder or something like Springs @Required or @Mandatory on attribute level to enforce Lombok to avoid offering the parameterless builder constructor and to provide a constructor with the mandatory parameters, as Joshua Bloch proposes?
I've tried many options from @Builder documentation but couldn't find a desirable solution.
What works for me is described as follows:
- defining a constructor for Person with mandatory parameters,
- overriding the builder constructor with a parameterized signature for the mandatory parameters.
It's a bit of boilerplate, which could possibly be avoided. See my solution applied on Joshua Bloch's example bellow.
/**
* Uncle Bobs builder example for constructors with many required & optional parameters,
* realized by lombok.
*
*/
@AllArgsConstructor(access=AccessLevel.PRIVATE) // Let lombok generate private c-tor with all parameters, as needed by @Builder.
@Builder(
builderClassName="Builder", // Cosmetic. Without this option, the builder class would have the name NutritionFactsBuilder.
toBuilder=true // Enabling creation of a builder instance based on a main class instance: NutritionFacts.
)
public class NutritionFacts {
// Required parameters
private int servingSize;
private int servings;
// Optional parameters
private int calories;
private int fat;
private int sodium;
private int carbohydrate;
/**
* A builder method demanding required parameters.
*/
public static Builder builder(int servingSize, int servings) {
return new NutritionFacts(servingSize, servings).toBuilder();
}
/**
* eclipse-created C-tor with required parameters.
* Can be public for instantiating, but doesn't have to.
*/
public NutritionFacts(int servingSize, int servings) {
super();
this.servingSize = servingSize;
this.servings = servings;
}
public static void main(String[] args) {
NutritionFacts cocaCola = NutritionFacts.builder(240, 8)
.calories(100)
.sodium(35)
.carbohydrate(27)
.build();
}
}