Search code examples
javagenericsconstructoreffective-javajavabuilders

Using Builder Constructor on Extended Class?


I'm implementing a Builder constructor as documented in Joshua Bloch's "Effective Java 2nd Edition. However, I'm running into a few complications when I try to extend the class and its builder. Essentially, the extended Builder in the extended child class has set field methods that return the parent Builder type, not the child builder type.

Of course, I can cast back to the ChildBuilder in the property build chain (as shown in my main method) but it is not seamless which defeats the purpose of the Builder, and it also forces me to segregate the parent setters and child setters.

I tried to use generics but it ended up becoming more verbose than the cast.

Is there a way I can consistently make the set methods on the builders return the builder type that was actually instantiated?

public class ParentObj {

    public static void main(String[] args) { 

    ChildObj childObj = ((ChildObj.ChildBuilder) (new ChildObj.ChildBuilder())
                .prop1(11)
                .prop2(21)
                .prop3(14))
                .prop4(12)
                .prop5(33)
                .build();
    }



    private int prop1;
    private int prop2;
    private int prop3;

    protected ParentObj(Builder builder) {
        this.prop1 = builder.prop1;
        this.prop2 = builder.prop2;
        this.prop3 = builder.prop3;
    }

    public class Builder { 
        private int prop1;
        private int prop2;
        private int prop3;
        public Builder prop1(int prop1) { this.prop1 = prop1; return this; } 
        public Builder prop2(int prop2) { this.prop2 = prop2; return this; } 
        public Builder prop3(int prop3) { this.prop3 = prop3; return this; } 

        public ParentObj build()
        {
            return new ParentObj(this);
        }
    }

    }

    private class ChildObj extends ParentObj { 

    private final int prop4;
    private final int prop5;

    private ChildObj(ChildBuilder childBuilder) {
        super(childBuilder);

    }

    public class ChildBuilder extends Builder {
        private int prop4;
        private int prop5; 
        public ChildBuilder prop4(int prop4) { this.prop4 = prop4; return this; } 
        public ChildBuilder prop5(int prop5) { this.prop5 = prop5; return this; } 

        public ChildObj build()  { 
            return new ChildObj(this);
        }
    }
}

Solution

  • Probably the best way would be to Override the parent builder methods.

    class ChildBuilder {
      public ChildBuilder prop1(int prop1){ 
        return (ChildBuilder) super.prop1(prop1);
      }
    }
    

    While this isn't exactly clean it will work for what you're trying to do.