3

I'm trying to implement the builder pattern for generating Apache XmlBeans objects.

I've generated builders for all my classes, which inherit from:

public abstract class Builder<T extends XmlObject> {

    protected T xmlObject;

    @SuppressWarnings("unchecked")
    public T build() {
        return (T) xmlObject.copy();
    }
}

Then I've created several builders like this one (Time2Save inherits from XmlObject):

public class Time2SaveBuilder extends Builder<Time2Save> {
    public Time2SaveBuilder(int amount, String unit) {
        xmlObject = Time2Save.Factory.newInstance();
        xmlObject.setUnit(unit);
        xmlObject.setValue(amount);
    }
}

It works perfectly fine. But the problem I have is that I don't really like the duplication of having to instantiate the xmlObject in each builder, and I'd rather do it in the abstract Builder class if possible. So I've tried adding this constructor to the Builder class:

@SuppressWarnings("unchecked")
public Builder() {
    xmlObject =  (T) T.Factory.newInstance();
}

And leaving the implementations like this:

public class Time2SaveBuilder extends Builder<Time2Save> {
    public Time2SaveBuilder(int amount, String unit) {
        xmlObject.setUnit(unit);
        xmlObject.setValue(amount);
    }
}

The problem is that I get the following error:

Exception in thread "main" java.lang.ClassCastException:
    org.apache.xmlbeans.impl.values.XmlAnyTypeImpl cannot be cast to
    a.b.c.d.Time2SaveDocument$Time2Save

I think XmlAnyTypeImpl is the result of calling the static XmlObject.Factory instead of one from the inheriting class (in this case, Time2Save). I'd like to know why this is happening (since I'm calling T.Factory and not XmlObject.Factory) and if there's any way to do this without duplicating the Factory call in each builder implementation. Thanks!

Sirs
  • 1,277
  • 17
  • 30
  • 1
    I wrote a related question/answer regarding the creation of builders for highly-extendable classes: http://programmers.stackexchange.com/questions/228939/how-to-improve-upon-blochs-builder-pattern-to-make-it-more-appropriate-for-use/228942#228942 – aliteralmind Apr 03 '14 at 20:46

2 Answers2

5

Assuming all the built objects have a no-arg constructor (and a bunch of other assumptions), you can use this idiom:

class Builder<SHAPE extends Shape> {

  public final SHAPE shape = build();

  @SuppressWarnings("unchecked")
  private SHAPE build() {
    try {
      ParameterizedType parent = 
             (ParameterizedType) getClass().getGenericSuperclass();   
      Class<?> arg = (Class<?>) parent.getActualTypeArguments()[0];
      return (SHAPE) arg.newInstance();
    } catch (ReflectiveOperationException e) {
      throw new RuntimeException(e);
    }
  }

  public SHAPE shape() { return shape; }
}

But be warned that this may hide a problem with your design.

Why you should anyway look for alternatives

Static methods are convenient when you know how to use them, otherwise they will bite you. Without cheating with reflection, static methods must be invoked by naming the enclosing class in the very source code, making the code effectively impossible to reuse. Also, static methods are not virtual, ie subclasses can't override the parent behavior - again, you can cheat by using reflection and dispatch the call yourself.

Why it doesn't work

The construct

public class Foo<T extends MyOuter> {
  public String bar() {
    return T.MyNested.aStaticMethod();
  }
}

accessing static members on a type variable, is rarely seen (demo). Method signatures are resolved at compile time, so the value of T has no chance to be read (and, even if it were, type arguments don't survive the compilation step in Java). The type searched is resolved recursively and T is replaced with its upper bound, leading to the class name XmlObject.Factory.

Raffaele
  • 20,627
  • 6
  • 47
  • 86
  • @Sirs I deleted the paragraph regarding the JSL because it was not relevant to this case. To be honest, I don't know if the spec allows a type variable to be used to access static members. Both Oracle and JDT compilers understand the syntax, but we'll have to investigate it :) So, thanks for your question – Raffaele Apr 04 '14 at 17:49
1

How about creating an abstract method that is being called from constructor? Just like this

public abstract class Builder<T extends XmlObject> {

   abstract protected T createObject();

   @SuppressWarnings("unchecked")
   public Builder() {
     xmlObject = (T) createObject();
   }
}

and then in Time2SaveBuilder you simply implement it to return proper factory instance. That should do the trick

Miron Balcerzak
  • 886
  • 10
  • 21
  • I'm trying to avoid having to explicitly call the Factory Method on each Builder implementation... Which your answer does not address, since I'll still have to put the Factory call on each implementation (and there's quite a lot of them). I'd like to be able to call it from the abstract builder, or at the very least to understand the reason why Java Generics prevent my implementation from working well... – Sirs Apr 03 '14 at 13:11
  • 1
    It's bad practice to call a non-final method in a ctor, because it will run before the child's ctor, and thus may find the object in an inconsistent state. One could warn about this in the documentation, but since it's in the design phase, I think you should look for alternatives – Raffaele Apr 03 '14 at 14:10