当我使用泛型时,我在某些类中出错。我指定了我收到错误的地方。我无法摆脱它。我尝试过演员阵容,以及我能想到的一切。
class UltraTable<O extends Object> {
public void setContent(Collection<O> collection) {
}
}
class ObjectA {
}
class AShell {
protected UltraTable<? extends ObjectA> tableA;
protected List<? extends ObjectA> getAObjects() {
return null; // the list is created here
}
}
class BShell extends AShell {
public BShell() {
tableA.setContent(getAObjects()); // THE ERROR IS HERE!
}
}
如果可能的话,如何仅在BShell
课程中进行更改才能使其工作?
我得到的错误信息是:
The method setContent(Collection<capture#1-of ? extends ObjectA>)
in the type UltraTable<capture#1-of ? extends ObjectA>
is not applicable for the arguments (List<capture#2-of ? extends ObjectA>)
更新
如果我按照Thomas Jung的说法更改我的代码,我在tableA
的构造函数和方法getAObjects()
的其他类中出错:
class ObjectX extends ObjectA {}
class XShell extends AShell {
public XShell() {
tableA = new UltraTable<ObjectX>();
tableA.setContent(getAObjects()); // THE SAME ERROR AS ABOVE
}
@Override
protected List<ObjectX> getAObjects() {
return null;
}
}
我经常有一个UltraTable和getAObjects()返回一个扩展BaseClass的DerivedClass列表。这应该适用于所有情况。
无论如何,我认为错误信息不合逻辑:“... <capture#1-of ? extends ObjectA>
不适用于参数(List<capture#2-of ? extends ObjectA>
)”
捕获#1 和捕获#2 都扩展了ObjectA!这有什么不对?
答案 0 :(得分:4)
像:
这样的定义protected UltraTable<? extends ObjectA> tableA;
protected List<? extends ObjectA> getAObjects(){}
没用。你不能用UltraTable<ObjectA>
做更多的事情。这样的定义仅在您使用它(作为参数)时才有用,而不是在您生成它(作为返回值)时。例如,定义:
public void setContent(Collection<? extends O> collection) {}
可能很有用。它现在可以使用O
及其所有子类型的集合。
UltraTable<O extends Object>
与UltraTable<O>
相同。所有O
都是java.lang.Object的子类。
这个编译。我希望它仍然有意义。
class UltraTable<O> {
public void setContent(Collection<O> collection) {}
}
class ObjectA {}
class AShell {
protected UltraTable<ObjectA> tableA;
protected List<ObjectA> getAObjects() {
return null; // the list is created here
}
}
class BShell extends AShell {
public BShell() {
tableA.setContent(getAObjects());
}
}
<强>更新强>
Java泛型是不变的。因此,唯一广泛适用的解决方案是以XShell和AShell中的泛型类型参数相同的方式更改代码。
class ObjectX extends ObjectA {}
class XShell extends AShell {
public XShell() {
tableA = new UltraTable<ObjectA>();
tableA.setContent(getAObjects());
}
@Override
protected List<ObjectA> getAObjects() {
return null;
}
}
这是有道理的。因为用XShell替换AShell是无效的:
AShell ashell;
List<ObjectA> aobjects = ashell.getAObjects();
aobjects.add(new ObjectA());
XShell xshell;
List<ObjectX> aobjects = xshell.getAObjects();
aobjects.add(new ObjectA()); //invalid: cannot cast to ObjectX
但是如果你不能像使用AShell一样使用XShell重新定义的方法,它会破坏Liskov substitution principle。
答案 1 :(得分:1)
假设tableA
是UltraTable<String>
而getAObjects
返回了UltraTable<Integer>
。你会破坏类型系统。
您可能想要做的是将AShell
:
class AShell<T extends ObjectA> {
private UltraTable<T> tableA;
public List<T> getAObjects() {
...
答案 2 :(得分:0)
我知道你为什么得到错误的答案。 众所周知,为了确保类型安全,存在类型化的类型,并且归结为如果使用通配符声明集合,编译器在运行时不知道它所拥有的实际对象的类是什么,即使在这种情况下,它是ObjectA的一些子类。
因此,编译器将允许您删除对象,因为它们已经安全存储,它知道它们的类型正确,但所有将对象添加到使用通配符类型声明的集合的方法都不起作用,但是comilier不知道实际的类已存储,所以不能保证类型安全。
由于脚注参数化类型仅存在于源中,因此编译器会强制执行这些类型并确保仅添加正确的对象。在运行时,任何List都是List,它无关紧要,因为编译器确保只添加正确的对象。
class AShell <T extends ObjectA>{
protected UltraTable<T> tableA;
protected List<T> getAObjects() {
return null; // the list is created here}
}
}
将源代码更改为上述方法,它将允许您使用objectA的任何子类创建UltraTableCollection表A,并返回声明为该类型的List。 当你的shellB调用getObjects方法时,garentted能够存储这个返回的List,因为它从最初定义方法的类继承List。 Ypou会得到一些编译错误,但它仍然会编译,编译器会告诉你,确保你将这个List存储在正确的位置,因为setContent方法,你现在得到的错误是由于getObjects(方法。
我知道它很长但它的泛型并且需要一些解释。