Home > Mobile >  Lombok @Builder for a class in multiple level of inheritance with mandatory fields
Lombok @Builder for a class in multiple level of inheritance with mandatory fields

Time:10-03

I'd like to implement Builder pattern for a class in deeper level of inheritance where some fields are mandatory (message, cause) and some optional (myOptField1, myOptField2...) by using Lombok @Builder and assuming that the parent class cannot be changed. So I've implemented my own builder() like this:

@Builder(toBuilder = true)
@Getter
public class MyException extends Exception {

    private int    myOptField1;
    private String myOptField2;

    public static MyExceptionBuilder builder(String message, Throwable cause) {
        return new MyException(message, cause).toBuilder();
    }

    public MyException(String message, Throwable cause) {
        super(message, cause);
    }
}

Then using of this class can be this way:

MyException
    .builder("Mandatory message", new SpecificException("specificCause"))
    .myOptField2("value")
    .build();

In IntelliJ Idea, everything seems to be fine but I get compilation error:

java: constructor MyException in class com.myproject.MyException cannot be applied to given types;
required: java.lang.String,java.lang.Throwable
found: int,java.lang.String
reason: actual and formal argument lists differ in length

So compiler can see only constructor generated by Lombok @Builder (=@AllArgsConstructor) and cannot see my constructor with parameters java.lang.String,java.lang.Throwable. Is there any better way how to solve this?

CodePudding user response:

The only thing which comes to my mind is to override build() & toBuilder() methods. Lombok generates its own constructor based on fields that do not suit your case, also Lombok doesn't know anything about fields from inherited class.

In my opinion, easier would be to implement your own Builder instead of hacking Lombok solution.


@Builder(toBuilder = true)
@Getter
public class MyException extends Exception {

    private int    myOptField1;
    private String myOptField2;

    public static MyExceptionBuilder builder(String message, Throwable cause) {
        return new MyException(message, cause).toBuilder();
    }

    public MyException(String message, Throwable cause) {
        super(message, cause);
    }

    MyExceptionBuilder toBuilder() {
        ....
    }

    public static class MyExceptionBuilder {

        public MyException build() {
           ....
        }
    }
}

CodePudding user response:

The point of Builder Pattern is Immutability, so your optional values should be final. You can put @Builder to the constructor like this:

@Getter
static class MyException extends Exception {

    private final int    myOptField1;
    private final String myOptField2;

    @Builder
    public MyException(String message, Throwable cause, int myOptField1, String myOptField2) {
        super(message, cause);
        this.myOptField1 = myOptField1;
        this.myOptField2 = myOptField2;
    }

}

MyException.builder().cause(new Exception()).message("").myOptField1(1).myOptField2("").build();

But it's not guaranteed the mandatory values. If it's important to you, you can make your own buider. In IntelliJ go to Refactor -> Delombok -> choose @Builder. You can see the generated code from @Builder and modify it by your own.

  • Related