How to run code after constructor in a Lombok builder

后端 未结 4 1647
庸人自扰
庸人自扰 2021-02-07 01:38

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 val         


        
相关标签:
4条回答
  • 2021-02-07 01:56

    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:

    • Add an init() method which is executed after each object construction automatically
    • Adding new fields do not require additional work (as it would be for an individually written constructor)
    • Possibility to add additional functionality (incl. the init() execution)
    • Retain the complete standard functionality the @Builder annotation brings
    • Don't expose an additional builder class

    Even 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.

    0 讨论(0)
  • 2021-02-07 02:10

    This works for me, not a complete solution, but quick and easy.

    @Builder
    @AllArgsConstructor
    public class Foo {
       @Builder.Default
       int bar = 42;
       Foo init() {
          // perform values initialisation
         bar = 451;   // replaces 314
         return foo;
       }
       static Foo test() {
           return new FooBuilder()  // defaults to 42
               .bar(314)  // replaces 42 with 314
               .build()
               .init();   // replaces 314 with 451
       }
    }
    
    0 讨论(0)
  • 2021-02-07 02:20

    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;
          }
       }
    }
    
    0 讨论(0)
  • 2021-02-07 02:23

    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.

    0 讨论(0)
提交回复
热议问题