Java泛型问题

时间:2009-11-15 16:51:41

标签: java generics

当我使用泛型时,我在某些类中出错。我指定了我收到错误的地方。我无法摆脱它。我尝试过演员阵容,以及我能想到的一切。

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!这有什么不对?

3 个答案:

答案 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)

假设tableAUltraTable<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(方法。

我知道它很长但它的泛型并且需要一些解释。