【问题标题】:Factory pattern and Generics工厂模式和泛型
【发布时间】:2015-10-02 08:27:40
【问题描述】:

我有时会遇到这种情况,但不确定我是否使用了不好的方法,或者我只是不知道如何解决它。

假设我有两个类和两个这样的 bean:

public class BeanOne {
    public void methodBeanOne() {
        //...
    }
}

public class BeanTwo {
    public void methodBeanTwo() {
        //...
    }
}

public class ClassOne {
    private BeanOne bean;

    public ClassOne(BeanOne bean) {
        this.bean = bean;
    }

    public void methodclassOne() {
        bean.methodBeanOne();
    }
}

public class ClassTwo {
    private BeanTwo bean;

    public ClassTwo(BeanTwo bean) {
        this.bean = bean;
    }

    public void methodClassTwo() {
        bean.methodBeanTwo();
    }
}

我想做一个通用的抽象类,所以我可以从ClassOneClassTwo中提取一些逻辑,以及一个带有通用方法的抽象bean:

public abstract class AbstractBean {
    public void commonMethod() {
        //...
    }
}

public class BeanOne extends AbstractBean {
    public void methodBeanOne() {
        //...
    }
}

public class BeanTwo extends AbstractBean {
    public void methodBeanTwo() {
        //...
    }
}

public abstract class AbstractClass<T extends AbstractBean> {
    protected T bean;

    public AbstractClass(T bean) {
        this.bean = bean;
    }

    public void commonClassMethod(){
        bean.commonMethod();
    }
}

public class ClassOne extends AbstractClass<BeanOne> {

    public ClassOne(BeanOne bean) {
        super(bean);
    }

    public void methodclassOne() {
        bean.methodBeanOne();
    }
}

public class ClassTwo extends AbstractClass<BeanTwo> {

    public ClassTwo(BeanTwo bean) {
        super(bean);
    }

    public void methodClassTwo() {
        bean.methodBeanTwo();
    }
}

到目前为止,一切都很好。

下一步是创建一个工厂以获取一个基于 enum 的实现,例如,这里是我开始出错的地方:

public class ClassFactory {

    public enum MyEnum {
        ONE, TWO;
    }

    private ClassFactory() {
    }

    public static AbstractClass newInstance(MyEnum value, AbstractBean bean) {
        switch(value){
        case ONE:
            return new ClassOne(bean);
        case TWO:
            return new ClassTwo(bean);
        default:
            throw new IllegalArgumentException();
        }
    }
}

这会产生以下编译错误:

The constructor ClassOne(AbstractBean) is undefined
The constructor ClassTwo(AbstractBean) is undefined

我也试过了:

public class ClassFactory {

    public enum MyEnum {
        ONE, TWO;
    }

    private ClassFactory() {
    }

    public static <T extends AbstractBean> AbstractClass<T> newInstance(MyEnum value, T bean) {
        switch(value){
        case ONE:
            return new ClassOne(bean);
        case TWO:
            return new ClassTwo(bean);
        default:
            throw new IllegalArgumentException();
        }
    }
}

然后我得到:

Type mismatch: cannot convert from ClassOne to AbstractClass<T>
Type mismatch: cannot convert from ClassTwo to AbstractClass<T>

而且我几乎被困在那里。我想我理解这个错误,但是,是否有可能创建这样一个工厂类试图避免强制转换

我也检查了this post,但不能完全理解它对我有什么帮助。

编辑:访客模式

好的,所以我尝试了上一篇文章中描述的访问者模式:

public interface Visitor<T> {
    T visit(BeanOne bean);

    T visit(BeanTwo bean);
}

public abstract class AbstractBean {
    public void commonMethod() {
        // ...
    }

    public abstract <T> T accept(Visitor<T> visitor);
}

public class BeanOne extends AbstractBean {
    public void methodBeanOne() {
        // ...
    }

    @Override
    public <T> T accept(Visitor<T> visitor) {
        return visitor.visit(this);
    }
}

public class BeanTwo extends AbstractBean {
    public void methodBeanTwo() {
        // ...
    }

    @Override
    public <T> T accept(Visitor<T> visitor) {
        return visitor.visit(this);
    }
}

public class ClassFactory {
    private ClassFactory() {
    }

    public static AbstractClass<? extends AbstractBean> newInstance(AbstractBean bean) {
        return bean.accept(new AbstractClassVisitor());
    }
}

public class AbstractClassVisitor implements Visitor<AbstractClass<? extends AbstractBean>> {

    @Override
    public AbstractClass<? extends AbstractBean> visit(BeanOne bean) {
        return ClassFactory.newInstance(bean);
    }

    @Override
    public AbstractClass<? extends AbstractBean> visit(BeanTwo bean) {
        return ClassFactory.newInstance(bean);
    }
}

但是像这样使用它:

AbstractBean bean = new BeanOne();
AbstractClass<? extends AbstractBean> clazz = ClassFactory.newInstance(bean);
clazz.commonClassMethod();

我收到以下异常:

Exception in thread "main" java.lang.StackOverflowError
    at test.AbstractClassVisitor.<init>(AbstractClassVisitor.java:3)
    at test.ClassFactory.newInstance(ClassFactory.java:9)
    at test.AbstractClassVisitor.visit(AbstractClassVisitor.java:7)
    at test.AbstractClassVisitor.visit(AbstractClassVisitor.java:1)
    at test.BeanOne.accept(BeanOne.java:10)
    at test.ClassFactory.newInstance(ClassFactory.java:9)
    at test.AbstractClassVisitor.visit(AbstractClassVisitor.java:7)
    at test.AbstractClassVisitor.visit(AbstractClassVisitor.java:1)
    at test.BeanOne.accept(BeanOne.java:10)
    at test.ClassFactory.newInstance(ClassFactory.java:9)
    at test.AbstractClassVisitor.visit(AbstractClassVisitor.java:7)
    at test.AbstractClassVisitor.visit(AbstractClassVisitor.java:1)
    at test.BeanOne.accept(BeanOne.java:10)
    ...

我明白为什么会这样了,是我遗漏了什么吗?

【问题讨论】:

  • 当你在ClassOne 内部调用super(bean); - 你在调用哪个类的构造函数?
  • @alfasin 我会在 AbstractClass 类中调用 public AbstractClass(T bean) 构造函数。
  • 在第一个代码示例中没有AbstractClass 并且ClassOne 没有扩展任何类。
  • @alfasin 没错,我的错。

标签: java generics factory


【解决方案1】:

根据您自己提供的问题,请参阅以下答案:https://stackoverflow.com/a/12630501/144302

同样的原则适用于您的问题:使用具有重载方法的工厂

public class ClassFactory {

  private ClassFactory() {
  }

  public static AbstractClass<BeanOne> newInstance(BeanOne bean) {
    return new ClassOne(bean);
  }

  public static AbstractClass<BeanTwo> newInstance(BeanTwo bean) {
    return new ClassTwo(bean);
  }
}

或者,正如答案中所指出的,应用双重分派之类的原则,在其中将方法 AbstractClass&lt;T&gt; newInstance() 添加到 AbstractBean 并在每个专业化中适当地实现它。例如

class BeanOne { /* ... */ 
   public AbstractBean<BeanOne> newInstance() {
      return ClassFactory.newInstance(this);
   }
}

终于可以在ClassFactory中添加如下方法

public static <T> AbstractClass<T> newInstance(AbstractBean<T> bean) {
   return bean.newInstance();
}

对于具体的优点和缺点,我鼓励你阅读另一个问题的完整答案(很好,我不想盲目复制别人的工作)。

【讨论】:

  • 我已经编辑了我的答案,添加了另一个线程中描述的访客模式方法,但现在我得到了一个StackOverflowError。我错过了什么吗?
  • AbstractClassVisitor 是做什么的?
  • 抱歉,忘记包含该类。
  • 在抽象访问者类中,您必须返回ClassOneClassTwo 的具体实例,而不是返回工厂方法,否则您将向接收@987654332 的工厂类添加特定方法@ 和 BeanTwo 作为参数。
【解决方案2】:

我认为这是因为构造函数参数public ClassOne(BeanOne bean)。您试图提供 AbstractBean 作为构造函数参数,它是 BeanOne 的父级。 Java 只允许对象扩大而不是缩小。尝试将父对象类型转换为特定的子对象,如下所示:

public static AbstractClass newInstance(MyEnum value, AbstractBean bean) {
    switch(value):
    case ONE:
        return new ClassOne((BeanOne)bean);
    case TWO:
        return new ClassTwo((BeanTwo)bean);
    default:
        throw new IllegalArgumentException();
}

但请确保在调用 newInstance 方法时提供正确的对象实例。 否则可能会发生 ClassCastException。

编辑:

public static AbstractClass newInstance(MyEnum value) {
    switch(value):
    case ONE:
        return new ClassOne(new BeanOne());
    case TWO:
        return new ClassTwo(new BeanTwo());
    default:
        throw new IllegalArgumentException();
}

【讨论】:

  • 是的,这就是为什么我想知道是否有办法在不进行强制转换的情况下完成此任务。
  • 然后按照上面的方法改变newInstance方法
  • 但是这样你只是删除了 bean 实例,我将传递给工厂并实例化一个新实例。我也许可以使用像new BeanOne(bean) 这样的复制构造函数。仍然困扰我的是警告AbstractClass is a raw type. References to generic type AbstractClass&lt;T&gt; should be parameterized,可以通过添加此AbstractClass&lt;?&gt; 来解决。尽管如此,这是正确的方法还是只是一些技巧来使其工作,即使它应该以另一种方式完成?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-19
  • 2016-12-26
  • 1970-01-01
相关资源
最近更新 更多