Blog coding and discussion of coding about JavaScript, PHP, CGI, general web building etc.

Sunday, January 17, 2016

Subclassing a Java Builder class

Subclassing a Java Builder class


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.

Answer by fge for Subclassing a Java Builder class


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...

Answer by Flavio for Subclassing a Java Builder class


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);          }      }      [...]  }  

Answer by gkamal for Subclassing a Java Builder class


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 {            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 {            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;      }  }  

Answer by Q23 for Subclassing a Java Builder class


Slightly more complicated, but here's a solution to your problem, based off of this blog post I found once when I was looking to do exactly this. This method requires all the non-leaf classes to be abstract, and all the leaf classes to be final, or Bad Things could happen.

public abstract class TopLevel {      protected int foo;      protected TopLevel() {      }      protected static abstract class Builder> {          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> extends TopLevel.Builder {          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 {          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();  }  

Answer by Stepan Vavra for Subclassing a Java Builder class


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> {        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.


Fatal error: Call to a member function getElementsByTagName() on a non-object in D:\XAMPP INSTALLASTION\xampp\htdocs\endunpratama9i\www-stackoverflow-info-proses.php on line 72

0 comments:

Post a Comment

Popular Posts

Powered by Blogger.