生成器模式与泛型和扩展类上的静态内部工厂

Sir*_*irs 3 java generics xmlbeans builder

我正在尝试实现构建器模式以生成Apache XmlBeans对象.

我为我的所有类生成了构建器,它继承自:

public abstract class Builder<T extends XmlObject> {

    protected T xmlObject;

    @SuppressWarnings("unchecked")
    public T build() {
        return (T) xmlObject.copy();
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我创建了几个这样的构建器(Time2Save继承自XmlObject):

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

它工作得很好.但我遇到的问题是我不喜欢在每个构建器中实例化xmlObject的重复,如果可能的话,我宁愿在抽象的Builder类中进行.所以我尝试将此构造函数添加到Builder类:

@SuppressWarnings("unchecked")
public Builder() {
    xmlObject =  (T) T.Factory.newInstance();
}
Run Code Online (Sandbox Code Playgroud)

并留下这样的实现:

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

问题是我收到以下错误:

Exception in thread "main" java.lang.ClassCastException:
    org.apache.xmlbeans.impl.values.XmlAnyTypeImpl cannot be cast to
    a.b.c.d.Time2SaveDocument$Time2Save
Run Code Online (Sandbox Code Playgroud)

我认为XmlAnyTypeImplXmlObject.Factory从继承类(在本例中Time2Save)调用静态而不是一个的结果.我想知道为什么会发生这种情况(因为我正在调用T.Factory而不是XmlObject.Factory),如果有任何方法可以在不重复每个构建器实现中的Factory调用的情况下执行此操作.谢谢!

Raf*_*ele 5

假设所有构建的对象都有一个no-arg构造函数(以及一堆其他假设),你可以使用这个成语:

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; }
}
Run Code Online (Sandbox Code Playgroud)

但请注意,这可能会隐藏您的设计问题.

为什么你应该寻找替代品

当您知道如何使用静态方法时,静态方法很方便,否则它们会咬你.如果不使用反射作弊,则必须通过在源代码中命名封闭类来调用静态方法,从而使代码实际上无法重用.此外,静态方法不是虚拟的,即子类不能覆盖父行为 - 再次,您可以通过使用反射作弊并自己调度调用.

为什么它不起作用

构造

public class Foo<T extends MyOuter> {
  public String bar() {
    return T.MyNested.aStaticMethod();
  }
}
Run Code Online (Sandbox Code Playgroud)

访问类型变量上的静态成员很少见(demo).方法签名在编译时被解析,因此T没有机会被读取(并且,即使它是,类型参数也不能在Java的编译步骤中存活).搜索的类型以递归方式解析,T并以其上限替换,从而导致类名XmlObject.Factory.