160

Give this Dr Dobbs article, and the Builder Pattern in particular, how do we handle the case of subclassing a Builder? Taking a cut-down version of the example where we want to subclass to add GMO labelling, a naive implementation would be:

public class NutritionFacts {                                                                                                    

    private final int calories;                                                                                                  

    public static class Builder {                                                                                                
        private int calories = 0;                                                                                                

        public Builder() {}                                                                                                      

        public Builder calories(int val) { calories = val; return this; }                                                                                                                        

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

    protected NutritionFacts(Builder builder) {                                                                                  
        calories = builder.calories;                                                                                             
    }                                                                                                                            
}

Subclass:

public class GMOFacts extends NutritionFacts {                                                                                   

    private final boolean hasGMO;                                                                                                

    public static class Builder extends NutritionFacts.Builder {                                                                 

        private boolean hasGMO = false;                                                                                          

        public Builder() {}                                                                                                      

        public Builder GMO(boolean val) { hasGMO = val; return this; }                                                           

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

    protected GMOFacts(Builder builder) {                                                                                        
        super(builder);                                                                                                          
        hasGMO = builder.hasGMO;                                                                                                 
    }                                                                                                                            
}

Now, we can write code like this:

GMOFacts.Builder b = new GMOFacts.Builder();
b.GMO(true).calories(100);

But, if we get the order wrong, it all fails:

GMOFacts.Builder b = new GMOFacts.Builder();
b.calories(100).GMO(true);

The problem is of course that NutritionFacts.Builder returns a NutritionFacts.Builder, not a GMOFacts.Builder, so how do we solve this problem, or is there a better Pattern to use?

Note: this answer to a similar question offers up the classes I have above; my question is regarding the problem of ensuring the builder calls are in the correct order.

Community
  • 1
  • 1
Ken Y-N
  • 14,644
  • 21
  • 71
  • 114

10 Answers10

204

You can solve it using generics. I think this is called the "Curiously recurring generic patterns"

Make the return type of the base class builder methods a generic argument.

public class NutritionFacts {

    private final int calories;

    public static class Builder<T extends Builder<T>> {

        private int calories = 0;

        public Builder() {}

        public T calories(int val) {
            calories = val;
            return (T) this;
        }

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

    protected NutritionFacts(Builder<?> builder) {
        calories = builder.calories;
    }
}

Now instantiate the base builder with the derived class builder as the generic argument.

public class GMOFacts extends NutritionFacts {

    private final boolean hasGMO;

    public static class Builder extends NutritionFacts.Builder<Builder> {

        private boolean hasGMO = false;

        public Builder() {}

        public Builder GMO(boolean val) {
            hasGMO = val;
            return this;
        }

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

    protected GMOFacts(Builder builder) {
        super(builder);
        hasGMO = builder.hasGMO;
    }
}
Radiodef
  • 37,180
  • 14
  • 90
  • 125
gkamal
  • 20,777
  • 4
  • 60
  • 57
  • A follow-up: if I wish to further subclass `GMOFacts`, is the code `public static class Builder extends NutritionFacts.Builder` and `public T GMO(boolean val)` correct? – Ken Y-N Jun 19 '13 at 02:49
  • 2
    Hmm, I think I'll have to either (a) post a new question, (b) redesign with `implements` instead of `extends`, or (c) throw everything away. I now have a weird compile error where `leafBuilder.leaf().leaf()` and `leafBuilder.mid().leaf()` is OK, but `leafBuilder.leaf().mid().leaf()` fails... – Ken Y-N Jun 19 '13 at 04:01
  • 1
    Not surprised to see this dangling ender. Last time I tried to extend a Builder, it turned into this radioactive slurry. Also, this assumes you have the ability to alter the parent code. I want to extend the Notification class in the Android SDK. I am not going to change their class to do that. – Rob Jan 03 '14 at 19:05
  • 12
    @gkamal `return (T) this;` results in an `unchecked or unsafe operations` warning. This is impossible to avoid, right? – Dmitry Minkovsky Jan 30 '15 at 16:26
  • That has been my experience. I have never been able to get rid of that warning. Always suppressed it. – tmn Feb 01 '15 at 19:52
  • 5
    To solve the `unchecked cast` warning, see the solution suggested bellow among the other answers: http://stackoverflow.com/a/34741836/3114959 – Stepan Vavra Jan 12 '16 at 11:00
  • @gkamal I really like your approach. However, can you add some example of a third level hierarchy? i.e. How would you implement `class GMOFactsTypeA extends GMOFacts` (including its builder)? I'm trying to do it, but `GMOFactsTypeA.hasGMO()` returns NutritionFacts.Builder, and therefore it's not possible to invoke `GMOFactsTypeA.hasGMO(val1).someAttributeTypeA(val2)`. Any idea? – user2957378 Feb 21 '16 at 01:27
  • 11
    Note that `Builder` is actually a **rawtype** - this should be `Builder>`. – Boris the Spider Mar 13 '16 at 07:14
  • 3
    @user2957378 the `Builder` for `GMOFacts` also need to be generic `Builder> extends NutritionFacts.Builder` - and this pattern can continue down as many levels as required. If you declare a non-generic builder then you cannot extend the pattern. – Boris the Spider Mar 13 '16 at 07:16
  • Forgot the `@Override` annotation for the subclass `build()` method – citizen conn Mar 16 '18 at 12:19
51

Just for the record, to get rid of the

unchecked or unsafe operations warning

for the return (T) this; statement as @dimadima and @Thomas N. talk about, following solution applies in certain cases.

Make abstract the builder which declares the generic type (T extends Builder in this case) and declare protected abstract T getThis() abstract method as follows:

public abstract static class Builder<T extends Builder<T>> {

    private int calories = 0;

    public Builder() {}

    /** The solution for the unchecked cast warning. */
    public abstract T getThis();

    public T calories(int val) {
        calories = val;

        // no cast needed
        return getThis();
    }

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

Refer to http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ205 for further details.

Stepan Vavra
  • 3,884
  • 5
  • 29
  • 40
  • Why is the `build()` method returning the NutrutionFacts here? – mvd Jun 24 '16 at 17:19
  • @mvd Because this is an answer to the question? In the subtypes, you'll override it such as `public GMOFacts build() { return new GMOFacts(this); }` – Stepan Vavra Jun 24 '16 at 20:51
  • The problem occur when we want to add 2nd child `BuilderC extends BuilderB` and `BuilderB extends BuilderA` when `BuilderB` isn't `abstract` – wrozwad Oct 17 '16 at 14:19
  • 2
    This is not an answer to the question, because the base class may not be abstract! – Roland Nov 30 '17 at 11:11
  • "Make abstract the builder which declares the generic type" - what if I wanted to use that builder directly? – K-- Jun 10 '20 at 17:53
22

Based off of a blog post, this approach requires all the non-leaf classes to be abstract, and all the leaf classes must be final.

public abstract class TopLevel {
    protected int foo;
    protected TopLevel() {
    }
    protected static abstract class Builder
        <T extends TopLevel, B extends Builder<T, B>> {
        protected T object;
        protected B thisObject;
        protected abstract T createObject();
        protected abstract B thisObject();
        public Builder() {
            object = createObject();
            thisObject = thisObject();
        }
        public B foo(int foo) {
            object.foo = foo;
            return thisObject;
        }
        public T build() {
            return object;
        }
    }
}

Then, you have some intermediate class that extends this class and its builder, and as many more as you need:

public abstract class SecondLevel extends TopLevel {
    protected int bar;
    protected static abstract class Builder
        <T extends SecondLevel, B extends Builder<T, B>> extends TopLevel.Builder<T, B> {
        public B bar(int bar) {
            object.bar = bar;
            return thisObject;
        }
    }
}

And, finally a concrete leaf class that can call all the builder methods on any of its parents in any order:

public final class LeafClass extends SecondLevel {
    private int baz;
    public static final class Builder extends SecondLevel.Builder<LeafClass,Builder> {
        protected LeafClass createObject() {
            return new LeafClass();
        }
        protected Builder thisObject() {
            return this;
        }
        public Builder baz(int baz) {
            object.baz = baz;
            return thisObject;
        }
    }
}

Then, you can call the methods in any order, from any of the classes in the hierarchy:

public class Demo {
    LeafClass leaf = new LeafClass.Builder().baz(2).foo(1).bar(3).build();
}
Dave Jarvis
  • 30,436
  • 41
  • 178
  • 315
Q23
  • 471
  • 3
  • 7
  • Do you know why the leaf classes need to be final? I'd like my concrete classes to be subclassable, but haven't found a way of making the compiler understand the type of `B`, it always turns out as the base class. – David Ganster Aug 21 '17 at 06:49
  • 1
    Note how the Builder class in LeafClass doesn't follow the same `> extends SomeClassParent.Builder` pattern that the intermediary SecondLevel class does, it declares specific types instead. You can't instate a class until you get to the leaf using the specific types, but once you do, you can't extend it any further because you're using the specific types and have abandoned the Curiously Recurring Template Pattern. This link might help: http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeParameters.html#FAQ106 – Q23 Aug 21 '17 at 13:17
7

You can override also the calories() method, and let it return the extending builder. This compiles because Java supports covariant return types.

public class GMOFacts extends NutritionFacts {
    private final boolean hasGMO;
    public static class Builder extends NutritionFacts.Builder {
        private boolean hasGMO = false;
        public Builder() {
        }
        public Builder GMO(boolean val)
        { hasGMO = val; return this; }
        public Builder calories(int val)
        { super.calories(val); return this; }
        public GMOFacts build() {
            return new GMOFacts(this);
        }
    }
    [...]
}
Flavio
  • 11,925
  • 3
  • 32
  • 36
  • Ah, I didn't know that, as I come from a C++ background. That's a useful approach for this small example, but with a full-blown class repeating all the methods becomes a pain, and an error-prone pain at that. +1 for teaching me something new, however! – Ken Y-N Jun 19 '13 at 00:31
  • It would seem to me that this does not solve anything. The reason (IMO) for sub-classing the parent is to reuse the parents methods without overriding them. If the classes are simply value objects with no real logic in the builder methods except to set a simple value, then calling the parent method in the overriding method has little to no value. – Developer Dude Dec 20 '17 at 19:05
  • The answer solves the problem described in the question: the code using the builder compiles with both orderings. Since one way compiles and the other does not, I guess there must be some value after all. – Flavio Dec 22 '17 at 10:55
3

There is also another way to create classes according to Builder pattern, which conforms "Prefer composition over inheritance".

Define an interface, that parent class Builder will inherit:

public interface FactsBuilder<T> {

    public T calories(int val);
}

The implementation of NutritionFacts is almost the same (except for Builder implementing 'FactsBuilder' interface):

public class NutritionFacts {

    private final int calories;

    public static class Builder implements FactsBuilder<Builder> {
        private int calories = 0;

        public Builder() {
        }

        @Override
        public Builder calories(int val) {
            return this;
        }

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

    protected NutritionFacts(Builder builder) {
        calories = builder.calories;
    }
}

The Builder of a child class should extend the same interface (except different generic implementation):

public static class Builder implements FactsBuilder<Builder> {
    NutritionFacts.Builder baseBuilder;

    private boolean hasGMO = false;

    public Builder() {
        baseBuilder = new NutritionFacts.Builder();
    }

    public Builder GMO(boolean val) {
        hasGMO = val;
        return this;
    }

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

    @Override
    public Builder calories(int val) {
        baseBuilder.calories(val);
        return this;
    }
}

Notice, that NutritionFacts.Builder is a field inside GMOFacts.Builder (called baseBuilder). The method implemented from FactsBuilder interface calls baseBuilder's method of the same name:

@Override
public Builder calories(int val) {
    baseBuilder.calories(val);
    return this;
}

There is also a big change in the constructor of GMOFacts(Builder builder). The first call in the constructor to parent class constructor should pass appropriate NutritionFacts.Builder:

protected GMOFacts(Builder builder) {
    super(builder.baseBuilder);
    hasGMO = builder.hasGMO;
}

The full implementation of GMOFacts class:

public class GMOFacts extends NutritionFacts {

    private final boolean hasGMO;

    public static class Builder implements FactsBuilder<Builder> {
        NutritionFacts.Builder baseBuilder;

        private boolean hasGMO = false;

        public Builder() {
        }

        public Builder GMO(boolean val) {
            hasGMO = val;
            return this;
        }

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

        @Override
        public Builder calories(int val) {
            baseBuilder.calories(val);
            return this;
        }
    }

    protected GMOFacts(Builder builder) {
        super(builder.baseBuilder);
        hasGMO = builder.hasGMO;
    }
}
R. Zagórski
  • 20,020
  • 5
  • 65
  • 90
3

A full 3 level example of multiple builder inheritance would look like this:

(For the version with a copy constructor for the builder see the second example below)

First level - parent (potentially abstract)

import lombok.ToString;

@ToString
@SuppressWarnings("unchecked")
public abstract class Class1 {
    protected int f1;

    public static class Builder<C extends Class1, B extends Builder<C, B>> {
        C obj;

        protected Builder(C constructedObj) {
            this.obj = constructedObj;
        }

        B f1(int f1) {
            obj.f1 = f1;
            return (B)this;
        }

        C build() {
            return obj;
        }
    }
}

Second level

import lombok.ToString;

@ToString(callSuper=true)
@SuppressWarnings("unchecked")
public class Class2 extends Class1 {
    protected int f2;

    public static class Builder<C extends Class2, B extends Builder<C, B>> extends Class1.Builder<C, B> {
        public Builder() {
            this((C) new Class2());
        }

        protected Builder(C obj) {
            super(obj);
        }

        B f2(int f2) {
            obj.f2 = f2;
            return (B)this;
        }
    }
}

Third level

import lombok.ToString;

@ToString(callSuper=true)
@SuppressWarnings("unchecked")
public class Class3 extends Class2 {
    protected int f3;

    public static class Builder<C extends Class3, B extends Builder<C, B>> extends Class2.Builder<C, B> {
        public Builder() {
            this((C) new Class3());
        }

        protected Builder(C obj) {
            super(obj);
        }

        B f3(int f3) {
            obj.f3 = f3;
            return (B)this;
        }
    }
}

And an example of usage

public class Test {
    public static void main(String[] args) {
        Class2 b1 = new Class2.Builder<>().f1(1).f2(2).build();
        System.out.println(b1);
        Class2 b2 = new Class2.Builder<>().f2(2).f1(1).build();
        System.out.println(b2);

        Class3 c1 = new Class3.Builder<>().f1(1).f2(2).f3(3).build();
        System.out.println(c1);
        Class3 c2 = new Class3.Builder<>().f3(3).f1(1).f2(2).build();
        System.out.println(c2);
        Class3 c3 = new Class3.Builder<>().f3(3).f2(2).f1(1).build();
        System.out.println(c3);
        Class3 c4 = new Class3.Builder<>().f2(2).f3(3).f1(1).build();
        System.out.println(c4);
    }
}


A bit longer version featuring a copy constructor for the builder:

First level - parent (potentially abstract)

import lombok.ToString;

@ToString
@SuppressWarnings("unchecked")
public abstract class Class1 {
    protected int f1;

    public static class Builder<C extends Class1, B extends Builder<C, B>> {
        C obj;

        protected void setObj(C obj) {
            this.obj = obj;
        }

        protected void copy(C obj) {
            this.f1(obj.f1);
        }

        B f1(int f1) {
            obj.f1 = f1;
            return (B)this;
        }

        C build() {
            return obj;
        }
    }
}

Second level

import lombok.ToString;

@ToString(callSuper=true)
@SuppressWarnings("unchecked")
public class Class2 extends Class1 {
    protected int f2;

    public static class Builder<C extends Class2, B extends Builder<C, B>> extends Class1.Builder<C, B> {
        public Builder() {
            setObj((C) new Class2());
        }

        public Builder(C obj) {
            this();
            copy(obj);
        }

        @Override
        protected void copy(C obj) {
            super.copy(obj);
            this.f2(obj.f2);
        }

        B f2(int f2) {
            obj.f2 = f2;
            return (B)this;
        }
    }
}

Third level

import lombok.ToString;

@ToString(callSuper=true)
@SuppressWarnings("unchecked")
public class Class3 extends Class2 {
    protected int f3;

    public static class Builder<C extends Class3, B extends Builder<C, B>> extends Class2.Builder<C, B> {
        public Builder() {
            setObj((C) new Class3());
        }

        public Builder(C obj) {
            this();
            copy(obj);
        }

        @Override
        protected void copy(C obj) {
            super.copy(obj);
            this.f3(obj.f3);
        }

        B f3(int f3) {
            obj.f3 = f3;
            return (B)this;
        }
    }
}

And an example of usage

public class Test {
    public static void main(String[] args) {
        Class3 c4 = new Class3.Builder<>().f2(2).f3(3).f1(1).build();
        System.out.println(c4);

        // Class3 builder copy
        Class3 c42 = new Class3.Builder<>(c4).f2(12).build();
        System.out.println(c42);
        Class3 c43 = new Class3.Builder<>(c42).f2(22).f1(11).build();
        System.out.println(c43);
        Class3 c44 = new Class3.Builder<>(c43).f3(13).f1(21).build();
        System.out.println(c44);
    }
}
v0rin
  • 510
  • 4
  • 11
2

If you don't want to poke your eye out on an angle bracket or three, or perhaps don't feel you... umm... I mean... cough... the rest of your team will quickly comprehend curiously recurring generics pattern, you can do this:

public class TestInheritanceBuilder {
  public static void main(String[] args) {
    SubType.Builder builder = new SubType.Builder();
    builder.withFoo("FOO").withBar("BAR").withBaz("BAZ");
    SubType st = builder.build();
    System.out.println(st.toString());
    builder.withFoo("BOOM!").withBar("not getting here").withBaz("or here");
  }
}

supported by

public class SubType extends ParentType {
  String baz;
  protected SubType() {}

  public static class Builder extends ParentType.Builder {
    private SubType object = new SubType();

    public Builder withBaz(String baz) {
      getObject().baz = baz;
      return this;
    }

    public Builder withBar(String bar) {
      super.withBar(bar);
      return this;
    }

    public Builder withFoo(String foo) {
      super.withFoo(foo);
      return this;
    }

    public SubType build() {
      // or clone or copy constructor if you want to stamp out multiple instances...
      SubType tmp = getObject();
      setObject(new SubType());
      return tmp;
    }

    protected SubType getObject() {
      return object;
    }

    private void setObject(SubType object) {
      this.object = object;
    }
  }

  public String toString() {
    return "SubType2{" +
        "baz='" + baz + '\'' +
        "} " + super.toString();
  }
}

and the parent type:

public class ParentType {
  String foo;
  String bar;

  protected ParentType() {}

  public static class Builder {
    private ParentType object = new ParentType();

    public ParentType object() {
      return getObject();
    }

    public Builder withFoo(String foo) {
      if (!"foo".equalsIgnoreCase(foo)) throw new IllegalArgumentException();
      getObject().foo = foo;
      return this;
    }

    public Builder withBar(String bar) {
      getObject().bar = bar;
      return this;
    }

    protected ParentType getObject() {
      return object;
    }

    private void setObject(ParentType object) {
      this.object = object;
    }

    public ParentType build() {
      // or clone or copy constructor if you want to stamp out multiple instances...
      ParentType tmp = getObject();
      setObject(new ParentType());
      return tmp;
    }
  }

  public String toString() {
    return "ParentType2{" +
        "foo='" + foo + '\'' +
        ", bar='" + bar + '\'' +
        '}';
  }
}

Key points:

  • Encapsulate the object in the builder so that inheritance prevents you from setting the field on the object held in the parent type
  • Calls to super ensure that logic (if any) added to the super type builder methods is retained in the sub types.
  • Down side is spurious object creation in the parent class(es)... But see below for a way to clean that up
  • Up side is much easier to understand at a glance, and no verbose constructor transferring properties.
  • If you have multiple threads accessing your builder objects... I guess I'm glad I'm not you :).

EDIT:

I found a way around the spurious object creation. First add this to each builder:

private Class whoAmI() {
  return new Object(){}.getClass().getEnclosingMethod().getDeclaringClass();
}

Then in the constructor for each builder:

  if (whoAmI() == this.getClass()) {
    this.obj = new ObjectToBuild();
  }

The cost is an extra class file for the new Object(){} anonymous inner class

Gus
  • 6,719
  • 6
  • 37
  • 58
1

One thing you could do is to create a static factory method in each of your classes:

NutritionFacts.newBuilder()
GMOFacts.newBuilder()

This static factory method would then return the appropriate builder. You can have a GMOFacts.Builder extending a NutritionFacts.Builder, that is not a problem. THE problem here will be to deal with visibility...

Dmitry Minkovsky
  • 36,185
  • 26
  • 116
  • 160
fge
  • 119,121
  • 33
  • 254
  • 329
0

I created a parent, abstract generic builder class that accepts two formal type parameters. First is for the type of object returned by build(), the second is the type returned by each optional parameter setter. Below are parent and child classes for illustrative purpose:

// **Parent**
public abstract static class Builder<T, U extends Builder<T, U>> {
    // Required parameters
    private final String name;

    // Optional parameters
    private List<String> outputFields = null;


    public Builder(String pName) {
        name = pName;
    }

    public U outputFields(List<String> pOutFlds) {
        outputFields = new ArrayList<>(pOutFlds);
        return getThis();
    }


    /**
     * This helps avoid "unchecked warning", which would forces to cast to "T" in each of the optional
     * parameter setters..
     * @return
     */
    abstract U getThis();

    public abstract T build();



    /*
     * Getters
     */
    public String getName() {
        return name;
    }
}

 // **Child**
 public static class Builder extends AbstractRule.Builder<ContextAugmentingRule, ContextAugmentingRule.Builder> {
    // Required parameters
    private final Map<String, Object> nameValuePairsToAdd;

    // Optional parameters
    private String fooBar;


    Builder(String pName, Map<String, String> pNameValPairs) {
        super(pName);
        /**
         * Must do this, in case client code (I.e. JavaScript) is re-using
         * the passed in for multiple purposes. Doing {@link Collections#unmodifiableMap(Map)}
         * won't caught it, because the backing Map passed by client prior to wrapping in
         * unmodifiable Map can still be modified.
         */
        nameValuePairsToAdd = new HashMap<>(pNameValPairs);
    }

    public Builder fooBar(String pStr) {
        fooBar = pStr;
        return this;
    }


    @Override
    public ContextAugmentingRule build() {
        try {
            Rule r = new ContextAugmentingRule(this);
            storeInRuleByNameCache(r);
            return (ContextAugmentingRule) r;
        } catch (RuleException e) {
            throw new IllegalArgumentException(e);
        }
    }

    @Override
    Builder getThis() {
        return this;
    }
}

This one has met my needs to satisfaction.

Jose Quijada
  • 558
  • 6
  • 13
-2

The following IEEE contribution Refined Fluent Builder in Java gives a comprehensive solution to the problem.

It dissects the original question into two sub-problems of inheritance deficiency and quasi invariance and shows how a solution to these two sub-problems opens for inheritance support with code reuse in the classical builder pattern in Java.

mc00x1
  • 25
  • 2
  • 4
    This answer does not contain any information to be helpful, does not contain at least a summary of the answer given in the link and leads to a link which requires login. – Sonata Apr 27 '20 at 08:57
  • This answer links to a peer-reviewed conference publication with an official publishing authority and official publishing and sharing procedure. – mc00x1 Jun 02 '20 at 03:22