java - 具有泛型的构建器模式和扩展类上的静态内部工厂

标签 java generics builder xmlbeans

我正在尝试实现用于生成 Apache XmlBeans 对象的构建器模式。

我已经为我所有的类生成了生成器,它们继承自:

public abstract class Builder<T extends XmlObject> {

    protected T xmlObject;

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

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

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

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

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

并留下这样的实现:

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

问题是我收到以下错误:

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

我认为 XmlAnyTypeImpl 是调用静态 XmlObject.Factory 而不是继承类的结果(在本例中,Time2Save ).我想知道为什么会这样(因为我调用的是 T.Factory 而不是 XmlObject.Factory),如果 有任何方法可以做到这一点,而无需在每个构建器实现中重复工厂调用。谢谢!

最佳答案

假设所有构建的对象都有一个无参数构造函数(以及一系列其他假设),您可以使用 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; }
}

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

为什么你无论如何都应该寻找替代品

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

为什么不起作用

结构

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

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

关于java - 具有泛型的构建器模式和扩展类上的静态内部工厂,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22836314/

相关文章:

listview - Flutter ListView.builder 创建一个无限滚动。如何将其设置为在内容结束时停止?

java - Java DSL RouteBuilder 的配置方法内的 Exchange/body 引用

java - BufferedReader.readLine()不读取并挂起系统(等待)

java - 将对象转换为 Xtext DSL

java - Eclipse:作为项目构建器的 Java 应用程序

android - 找不到 builder.jar (com.android.tools.build :builder:3. 0.1)

java - 将数组值替换为 HashMap 值

c# - 如何缩短 List<List<KeyValuePair<string, string>>>?

c# - List<object>.RemoveAll - 如何创建合适的 Predicate

java - Java 1.8.0_65 的推断类型问题