我有一个带有方法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>
类型的容器,其中S
是T
的祖先。是否可以使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>
,但我会觉得非常非常有罪。)
答案 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是最好的选择。