Enhancing Lombok's Builder with custom setter method and enhance the existing build method without reimplementation

640 Views Asked by At

I'm using Lombok's @Builder annotation and need to add a custom setter method, as well as enhancing the build() method.

However, I'm stuck with two solutions where none covers both requirements at once and one contradicts the other.

They vary between the direct Builder override and an inherited Builder.

The code contains those two variants and describes what is and what isn't working.

public class LombokCustomBuilderWithCustomSetterAndBuildMethodExamples {


    /**
     * Without builder inheritance
     */
    @Builder
    public static class ExampleA {

        private String someField;

        /**
         * Directly overwrites the Lombok builder
         */
        private static class ExampleABuilder {

            /**
             * this works
             */
            public ExampleABuilder someCustomSetter(String someValue) {
                this.someField = someValue.toUpperCase();
                return this;
            }

            /**
             * super.builder() not available, as we have overwritten the Lombok's build() method entirely.
             * We would need to re-implement the functionality by ourselves
             */
            public ExampleA build() {
                ExampleA myCreatedObject = super.build();

                if (myCreatedObject.someField == null) throw new RuntimeException("Some validation failed");

                return myCreatedObject;
            }

        }
    }


    /**
     * With child and parent builder inheritance
     */
    @Builder
    public static class ExampleB {

        private String someField;

        private static class CustomExampleBBuilder extends ExampleBBuilder {

            /**
             * this does not work, as this.someField now has private access
             */
            public CustomExampleBBuilder someCustomSetter(String someValue) {
                this.someField = someValue.toUpperCase();
                return this;
            }

            /**
             * This works, super.build() is available, we are using the Lombok's build() result
             * and won't have to rewrite it
             */
            @Override
            public ExampleB build() {
                ExampleB myCreatedObject = super.build();

                if (myCreatedObject.someField == null) throw new RuntimeException("Some validation failed");

                return myCreatedObject;
            }

        }
    }
}

On one hand, I'd need the inheritance so the build() method does not need to be reimplemented, on the other hand I cannot access the field of the class I need to set with the custom setter method.

How can I reuse the existing build() method's result after the object has been built and at the same time have my custom setter method?

1

There are 1 best solutions below

0
Marian Klühspies On

I solved it by combining both approaches.

  1. The default builder override to add the custom setter
  2. The inherited CustomBuilder to reuse the build() method

The only thing that was required to be done in addition was to also override the builder() method, so the CustomBuilder will be used instead of the default Builder


import lombok.Builder;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class LombokCustomBuilderWithCustomSetterAndBuildMethodExamplesTest {

    public static final String APPENDIX = " - modified by custom build()";

    @Test
    void test() {
        assertEquals("TEST" + APPENDIX, ExampleC.builder().someCustomSetter("test").build().someField);
    }

    /**
     * Combination of both
     */
    @Builder
    public static class ExampleC {

        private String someField;

        /**
         * So the CustomExampleCBuilder will be provided instead
         * of the ExampleCBuilder 
         */
        public static CustomExampleCBuilder builder() {
            return new CustomExampleCBuilder();
        }

        /**
         * Create a base builder override to only add "someCustomeSetter" and keep
         * the rest of Lombok's methods by not overriding them
         */
        private static class ExampleCBuilder {

            public ExampleCBuilder someCustomSetter(String someValue) {
                this.someField = someValue.toUpperCase();
                return this;
            }

        }

        /**
         * Create a child to use the Lombok .build() and enhance it
         */
        private static class CustomExampleCBuilder extends ExampleCBuilder {
            @Override
            public ExampleC build() {
                ExampleC myCreatedObject = super.build();

                if (myCreatedObject.someField == null) throw new RuntimeException("Some validation failed");

                myCreatedObject.someField += APPENDIX;

                return myCreatedObject;
            }
        }
    }

}