Benny Bottema
Benny Bottema

Reputation: 11493

Is it possible to use Lombok @Builder, starting from static method?

I want Lombok to take care of my builder API, while also having a constructor to start with.

I started out with a constructor on the @Data class combined with @Builder(toBuilder = true), but that left me with forcing invalid or dummy values on final fields as well as a less expressive fluent API. I finally solved my situation using a static method, but I'm hoping Lombok has a better solution for my use case.

API using toBuilder

fooHandler.accept(new TweakedFoo(Foo.class, Mode.QUICK).toBuilder()
          .mappingOutcomeFor(FooOutcome.class)
          .mappingOutcome(toEvent(BarOutcome.class))
          .build()));

API using static method

fooHandler.accept(tweakFoo(Foo.class, Mode.QUICK)
          .mappingOutcomeFor(FooOutcome.class)
          .mappingOutcome(toEvent(BarOutcome.class))
          .build()));

See how the second setup flows better?

Respective Lombok setup (simplified)

@Data
@Builder(toBuilder = true)
public class TweakedFoo {

    private final Class<Foo> from;
    private final Mode mode;
    private final Class<?> to;

    public TweakedFoo(Class<Foo> from, Mode mode) {
        this.from = from;
        this.mode = mode;
        this.to = null; // eww
    }
}

And:

@Data
@Builder(builderMethodName = "useTweakedFooDotTweakedFooInsteadPlease")
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class TweakedFoo {

    private final Class<Foo> from;
    private final Mode mode;
    private final Class<?> to;

    public static TweakedFooBuilder tweakFoo(Class<Foo> from, Mode mode) {
        return TweakedFoo.useTweakedFooDotTweakedFooInsteadPlease()
                         .from(from)
                         .mode(mode);
    }

}

The actual parameters don't make much sense here, but this setup illustrates my real-world use case.

Not only is the second approach more concise, it needs no dummy constructor field initialization and also it hides the constructor so you can't get an instance other than through the builder. However, the second approach requires me to obscure the builder starting method Lombok generates in favor of my own static method.

Is there a better way with Lombok?

Upvotes: 1

Views: 5408

Answers (1)

Jan Rieke
Jan Rieke

Reputation: 8042

You can customize your builder() method by simply implementing it yourself:

@Data
@Builder
public class TweakedFoo {
    // ...
    public static TweakedFooBuilder builder(Class<Foo> from, Mode mode) {
        return new TweakedFooBuilder()
            .from(from)
            .mode(mode);
    }
    // ...
}

Lombok will not generate another builder() method in this case, because it recognizes the existing method with the same name. If you want the method to be named differently, e.g. tweakFoo, use @Builder(builderMethodName="tweakFoo").

Upvotes: 2

Related Questions