2009-11-15 97 views
0

当我使用泛型时,在某些类中出现错误。我指定了哪里出错。我无法摆脱它。我尝试了演员阵容,以及我能想到的所有事情。Java泛型的问题

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

更新:

如果我改变我的代码托马斯·荣格说,我在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都扩展对象A!这里有什么问题?

回答

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} 
} 

}

改变源代码以上面的方法,它可以让你创建一个UltraTableCollection TABLEA与对象A的任何子类,并返回被声明为类型的列表。 当你的shellB调用getObjects方法时,能够存储这个返回的List,因为它继承了原来定义方法的类中的List。 Ypou会得到一些编译错误,但它仍然会编译,编译器会说你确定你将这个List存储在正确的位置,这是因为setContent方法,你现在得到的错误是由于getObjects () 方法。

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

+0

ps。做很多单元测试。 – chrisg 2009-11-16 02:24:27

1

假设tableAUltraTable<String>getAObjects返回UltraTable<Integer>。你会打破类型系统。

也许你可能想要做的是genericise AShell

class AShell<T extends ObjectA> { 

    private UltraTable<T> tableA; 

    public List<T> getAObjects() { 
     ... 
+0

我试着像你说的,但在我的情况下,表和列表中的对象并不总是相同的类型。例如,我有一个'UltraTable ','getAObjects()'返回'DerivedClass'列表,它扩展了'BaseClass' – 2009-11-15 21:54:42

+0

因此,也许'UltraTable.setContents'应该使用'Collection '。 – 2009-11-16 00:14:08

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; 
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 

但是,如果你不能与XSHELL以同样的方式重新定义为与一的一枚方法工作,它打破了Liskov substitution principle

+0

如果我照你说的,我会在其他地方的错误,如: 类_对象扩展对象A {} 类XSHELL扩展{的一枚公共 XSHELL(){ 表A =新UltraTable (); // tableA.setContent(getAObjects()); } @Override protected List getAObjects(){ return null; } } – 2009-11-15 21:34:59

+0

您能否将此添加到您的问题中?我无法在评论中解读它。 – 2009-11-16 08:05:06

+0

我不能让它工作,除非像chrisg说的那样向AShell添加类型参数('class AShell ')。 – 2009-11-19 08:24:52