通用容器的通用工厂

时间:2011-11-24 18:08:28

标签: java generics factory-pattern

我有一个带有方法Factory<T>的通用抽象类createBoxedInstance(),它返回由通用容器T中包含的createInstance()实现创建的Box<T>实例

abstract class Factory<T> {
    abstract T createInstance();

    public final Box<T> createBoxedInstance() {
        return new Box<T>(createInstance());
    }

    public final class Box<T> {
        public final T content;

        public Box(T content) {
            this.content = content;
        }
    }
}

在某些方面,我需要一个Box<S>类型的容器,其中ST的祖先。是否可以使createBoxedInstance()本身具有通用性,以便它返回Box<S>的实例,其中调用者选择S?遗憾的是,如下定义函数不起作用类型参数不能使用超级关键字声明,只有使用

public final <S super T> Box<S> createBoxedInstance() {
    return new Box<S>(createInstance());
}

我看到的唯一选择是让所有需要Box<S>实例的地方接受Box<? extends S>,这使得容器的内容成员可以分配给S

如果没有将T的实例重新装入Box<S>类型的容器中,是否有某种解决方法? (我知道我可以将Box<T>投射到Box<S>,但我会觉得非常非常有罪。)

3 个答案:

答案 0 :(得分:2)

尝试重写您的其他代码,以便不使用Box<S>,而是使用Box<? extends S>,因此它也会接受Box<T>。这样,您明确表示Box也可能包含S的子类。

如果您制作Box static

,以下内容也可以使用
public static <S, T extends S> Box<S> createBoxedInstance(Factory<T> factory) {
  return new Box<S>(factory.createInstance());
}

但是,它可能无法对S进行类型推断,此时您需要:

public static <S, T extends S> Box<S> createBoxedInstance(Factory<T> factory, S dummy) {
  return new Box<S>(factory.createInstance());
}

另一个非常明显的变体:

public static <S, T extends S> Box<? extends S> createBoxedInstance(Test<T> factory, S dummy) {
  return factory.createBoxedInstance(); // Actually a Box<T>
}

答案 1 :(得分:0)

你可以在某种程度上实现你想要的东西:

public final <S extends Box<? super T>> S createBoxedInstance() {
    // sadly we can't capture the wildcard in the bound of S
    // this means we have to cast, but at least it's local..
    // *should* this cast ever break due to reification, we
    // can fix it here and be good to go
    @SuppressWarnings("unchecked")
    S box = (S) new Box<T>(createInstance());
    return box;
}

Factory<Integer> factory = getFactory();
Box<Number> box1 = factory.createBoxedInstance();
Box<Object> box2 = factory.createBoxedInstance();

我确实认为你自己的Box<? extends S> - 替代是正确的方法。但是,如果这会强制客户端代码充满通配符,则上述可能更可取。

答案 2 :(得分:0)

您无法做到这一点(或者您必须使用奇怪的转换)的原因如下 想象一下Box的内容而不是最终的内容也有一个二传手。另外,假设类型S是R和T的超类型。以下代码在没有任何强制转换的情况下有效。

Box<T> boxT = new Box<T>(objectT);
Box<S> boxS = boxT;
S objectR = new R();
boxS.set(objectR);

该代码在没有警告的情况下是有效的,除了setter在运行时会因意外异常而失败(意外的意思是可以抛出一个转换异常并不明显),因为你不能分配一个类型的东西R到T型的东西。

你可以在http://www.angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html#Topic3阅读更多内容(但附近有阿司匹林,你会头疼)

正如很多人所说,可能在其他代码中使用box是最好的选择。