I have a class that I want to use Lombok.Builder and I need pre-process of some parameters. Something like this:
@Builder
public class Foo {
public String val1;
public int val2;
public List<String> listValues;
public void init(){
// do some checks with the values.
}
}
normally I would just call init()
on a NoArg constructor, but with the generated builder I'm unable to do so. Is there a way for this init
be called by the generated builder? For example build()
would generate a code like:
public Foo build() {
Foo foo = Foo(params....)
foo.init();
return foo;
}
I'm aware that I can manually code the all args
constructor, that the Builder will call through it and I can call init
inside there.
But that is a sub-optimal solution as my class will likely have new fields added every once in a while which would mean changing the constructor too.
Create a Static Factory Method with @AllArgsConstructor When staticName is used, Lombok marks the generated constructor as private and creates a static factory method - of() in our case - that will be used to construct all User objects.
Lombok's @Builder annotation is a useful technique to implement the builder pattern that aims to reduce the boilerplate code. In this tutorial, we will learn to apply @Builder to a class and other useful features. Ensure you have included Lombok in the project and installed Lombok support in the IDE.
When we annotate a class with @Builder, Lombok creates a builder for all instance fields in that class. We've put the @Builder annotation on the class without any customization. Lombok creates an inner static builder class named as StudentBuilder. This builder class handles the initialization of all fields in Student.
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()
In Foo
you could manually add a constructor, have that do the initialization, and put @Builder
on the constructor. I know that you already know this, but I think it is the right solution, and you won't forget to add the parameter since you do want to use the code in the builder anyway.
Disclosure: I am a lombok developer.
After much trial and end error I found a suitable solution: extend the generate builder and call init()
myself.
Example:
@Builder(toBuilder = true, builderClassName = "FooInternalBuilder", builderMethodName = "internalBuilder")
public class Foo {
public String val1;
public int val2;
@Singular public List<String> listValues;
void init() {
// perform values initialisation
}
public static Builder builder() {
return new Builder();
}
public static class Builder extends FooInternalBuilder {
Builder() {
super();
}
@Override public Foo build() {
Foo foo = super.build();
foo.init();
return foo;
}
}
}
I just stumbled upon the same issue. But additionally, I wanted to add an method buildOptional()
to the builder to not repeat Optional.of(Foo)
each time I need it. This did not work with the approach posted before because the chained methods return FooInternalBuilder
objects; and putting buildOptional()
into FooInternalBuilder
would miss the init()
method execution in Builder
...
Also, I personally did not like the presence of 2 builder classes.
Here is what I did instead:
@Builder(buildMethodName = "buildInternal")
@ToString
public class Foo {
public String val1;
public int val2;
@Singular public List<String> listValues;
public void init(){
// do some checks with the values.
}
/** Add some functionality to the generated builder class */
public static class FooBuilder {
public Optional<Foo> buildOptional() {
return Optional.of(this.build());
}
public Foo build() {
Foo foo = this.buildInternal();
foo.init();
return foo;
}
}
}
You can do a quick test with this main method:
public static void main(String[] args) {
Foo foo = Foo.builder().val1("String").val2(14)
.listValue("1").listValue("2").build();
System.out.println(foo);
Optional<Foo> fooOpt = Foo.builder().val1("String").val2(14)
.listValue("1").listValue("2").buildOptional();
System.out.println(fooOpt);
}
Doing so let's you add what I want:
init()
method which is executed after each object construction automaticallyinit()
execution)@Builder
annotation bringsEven if you solved your problem before I like to share this as the solution. It is a bit shorter and adds a (for me) nice feature.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With