另一个javagenerics问题

我有以下课程:

interface Able{/* ... */} class A implements Able{/* ... */} 

我有

 Map as; as = new HashMap(); 

为什么以下会导致错误:

 as.put("a", new A()); 

有任何想法吗?

对javagenerics的引用很好( jdk站点 )。

确实@Oli_Charlesworth给出了一个很好的答案,但也许这个会更完整。

在一个Collection Collection你不能插入任何正确的东西。

如果你有

 class A implements Able {...} 

 class B implement Able {...} 

然后, Collection Collection是两者的超级类型:

 Collection Collection 

因此,写一些声明是合法的

 //Code snippet 01 Collection< ? extends Able > list; Collection listA; Collection listB; list = listA; list = listB; 

这确实是通配符Collection Collection存在。

但是,事情变得越来越有趣:

Collection您只能插入A (包括子类)的对象。 对于Collection 。 在这两者中你都无法添加只是Able东西。 例如 :

 //Code snippet 02 listA.add( new A() ); //valid at compile-time listA.add( new B() ); //not valid at compile-time listB.add( new B() ); //valid at compile-time listB.add( new A() ); //not valid at compile-time 

因此,如果您将我们在code snippets 01 & 02看到的内容分组,您将理解编译器绝对不可能接受如下语句:

 Collection< ? extends Able > list; list.add( new A() ); //not allowed, will work only if list is List list.add( new B() ); //not allowed, will work only if list is List 

所以是的,超级型Collection< ? extends Able > Collection< ? extends Able >不接受添加任何内容。 更一般的类型提供了子类型的function,因此,子类型的function较少。 在这里,我们失去了添加A对象和B对象的能力。 这些function将在以后的层次结构中发生……它甚至意味着我们无法在超类Collection< ? extends Able >添加任何内容Collection< ? extends Able > Collection< ? extends Able >

补充说明:

此外,请注意,在Collection您可以添加任何您想要的内容:

 Collection< Able > list; list.add( new A() ); //valid list.add( new B() ); //valid 

但是, Collection不是CollectionCollection的超类。 与任何inheritance关系一样,这意味着子类可以执行超类可以执行的任何操作,因为inheritance是特化。 因此,这意味着我们可以将A对象和B对象添加到子类CollectionCollection ,但事实并非如此。 因为它不是超级类,你不能拥有:

 Collection list; Collection listA; Collection listB; list = listA; //not valid because there is no inheritance hierarchy list = listB; //not valid because there is no inheritance hierarchy 

请注意,inheritance是一种超级联系(泛化/专业化),而集合定义了一个meronimic关系(容器/容器)。 将它们两者正式结合起来是一件令人头痛的问题,尽管人类模糊生物很容易使用它,例如在法语词汇中 : synecdocque 。 🙂

来自http://download.oracle.com/javase/tutorial/extra/generics/wildcards.html :

像往常一样,为使用通配符的灵活性付出了代价。 这个价格是写入[基于通配符的容器]现在是非法的。 例如,这是不允许的:

 public void addRectangle(List shapes) { shapes.add(0, new Rectangle()); // Compile-time error! } 

您应该能够弄清楚为什么不允许上面的代码。 shapes.add()的第二个参数的类型是? extends Shape ? extends Shape? extends Shape一个未知子类型。 由于我们不知道它是什么类型,我们不知道它是否是Rectangle的超类型; 它可能是也可能不是这样的超类型,所以在那里传递一个Rectangle是不安全的。

理解该问题的一个好方法是阅读通配符的含义:

 Map as; 

“带有String类型键的映射和一个扩展Able的类型的值。”

不允许添加操作的原因是因为它们“打开门”以在集合中引入不同类型,这将与打字系统冲突。 例如

 class UnAble implements Able; Map unableMap = new HashMap(); Map ableMap = unableMap; ableMap.put("wontwork",new A()); // type mismatch: insert an A-type into an Unable map 

正确使用通配符结构将是:

 Result processAble(Map) { ... read records & do something ... } Map ableMap = new HashMap; ableMap.put("willwork",new A()); processAble(as); processAble(unableMap); // from the definition above 

宣言

 Map as; 

表示“任何带有字符串键的映射,值是Able的子类型”。 因此,例如,您可以执行以下操作:

 Map as = new HashMap(); 

现在让我们来看看这个例子:

 Map as = new HashMap(); as.put("key", new A() ); 

如果它是正确的,你将完成HashMap的内容{“key”,new A()} – 这是类型错误!

Collection各种集合的超类型 。 它不是一个可以容纳任何类型 集合。 至少那是我对整个概念的误解。

我们可以在不关心generics类型的地方使用它,就像在这个例子中一样:

 public static void print(Collection aCollection) { for (Object o:aCollection) { System.out.println(o); } } 

如果我们选择了签名:

 public static void print(Collection aCollection) 

我们将自己局限于Collection类型的Collection – 换句话说,这样的方法不接受Collection类型的值。

因此Collection类型不是可以采用任何类型的集合。 它只需要未知类型 。 因为我们不知道那种类型(它的未知;)),所以我们永远不能添加一个值,因为java中没有类型是未知类型的子类。

如果我们添加边界(如 ),则类型仍然未知。

您正在寻找地图的声明,其值都实现了Able接口。 正确的声明很简单:

 Map map; 

假设我们有两个类型AB ,它们是Able子类和另外两个附图

 Map aMap; Map bMap; 

并且想要一个返回其值实现Able接口的map的方法: 然后我们使用通配符:

 public Map createAorBMap(boolean flag) { return flag ? aMap: bMap; } 

(再次使用约束,我们不能将新的键/值对添加到此方法返回的地图中)。

您不能在使用通配符“?”声明的集合中插入任何类型的任何对象。

你只能插入“null”

一旦将集合声明为List,编译器就无法知道添加SubAble是安全的。

如果Collection分配给Collection怎么办? 这将是一个有效的赋值,但添加SubAble会污染集合。

如何将元素添加到通配符通用集合中?