How to run code after constructor in Lombok builder

I have a class that I want to use Lombok.Builder, and I need a preliminary process of some parameters. Something like that:

@Builder public class Foo { public String val1; public int val2; public List<String> listValues; public void init(){ // do some checks with the values. } } 

Usually I just called init() in the NoArg constructor, but I could not do this with the constructor created. Is there a way for this init invoke the generated builder? For example, build() will generate code like:

 public Foo build() { Foo foo = Foo(params....) foo.init(); return foo; } 

I know that I can manually encode the all args constructor, which Builder will call through it, and I can call init inside.

But this is not an optimal solution, since my class will most likely add new fields every time, which will mean a change in the constructor too.

+5
source share
3 answers

After a big trial and final error, I found a suitable solution: expand the generation generator and call init() yourself.

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; } } } 
+4
source

In Foo you can manually add a constructor, initialize, and put @Builder in the constructor. I know that you already know this, but I think this is the right solution, and you will not forget to add this parameter, since you still want to use the code in the builder.

Disclosure: I am a lombok developer.

+2
source

I just stumbled upon the same problem. But also, I wanted to add the buildOptional() method to the builder so as not to repeat Optional.of(Foo) every time I need it. This did not work previously because chained methods return FooInternalBuilder objects; and placing buildOptional() in FooInternalBuilder skip the execution of the init() method in Builder ...

In addition, I personally did not like the presence of two constructor 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 perform a quick test using this basic 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); } 

Let's do this to add what I want:

  • Add an init() method that starts automatically after each object construct.
  • Adding new fields does not require additional work (as it would be for an individually written constructor)
  • Ability to add additional functions (including execution of init() )
  • Save the full standard functionality annotation @Builder brings
  • Do not set an extra class class

Even if you solved your problem before I wanted to share it as a solution. This is a little shorter and adds (for me) a nice feature.

+1
source

All Articles