另一个javagenerics问题
我有以下课程:
interface Able{/* ... */} class A implements Able{/* ... */}
我有
Map as; as = new HashMap();
为什么以下会导致错误:
as.put("a", new A());
有任何想法吗?
对javagenerics的引用很好( jdk站点 )。
确实@Oli_Charlesworth给出了一个很好的答案,但也许这个会更完整。
在一个Collection extends Able>
Collection extends Able>
你不能插入任何正确的东西。
如果你有
class A implements Able {...}
和
class B implement Able {...}
然后, Collection extends Able>
Collection extends Able>
是两者的超级类型:
Collection Collection
因此,写一些声明是合法的
//Code snippet 01 Collection< ? extends Able > list; Collection listA; Collection listB; list = listA; list = listB;
这确实是通配符Collection extends Able>
Collection extends Able>
存在。
但是,事情变得越来越有趣:
在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
不是Collection
和Collection
的超类。 与任何inheritance关系一样,这意味着子类可以执行超类可以执行的任何操作,因为inheritance是特化。 因此,这意味着我们可以将A对象和B对象添加到子类Collection
和Collection
,但事实并非如此。 因为它不是超级类,你不能拥有:
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 extends Shape> 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
我们将自己局限于Collection
类型的Collection
– 换句话说,这样的方法不接受Collection
类型的值。
因此Collection>
类型不是可以采用任何类型的集合。 它只需要未知类型 。 因为我们不知道那种类型(它的未知;)),所以我们永远不能添加一个值,因为java中没有类型是未知类型的子类。
如果我们添加边界(如 extends Able>
),则类型仍然未知。
您正在寻找地图的声明,其值都实现了Able
接口。 正确的声明很简单:
Map map;
假设我们有两个类型A
和B
,它们是Able
子类和另外两个附图
Map aMap; Map bMap;
并且想要一个返回其值实现Able
接口的map的方法: 然后我们使用通配符:
public Map createAorBMap(boolean flag) { return flag ? aMap: bMap; }
(再次使用约束,我们不能将新的键/值对添加到此方法返回的地图中)。
您不能在使用通配符“?”声明的集合中插入任何类型的任何对象。
你只能插入“null”
一旦将集合声明为List,编译器就无法知道添加SubAble是安全的。
如果Collection
分配给Collection
怎么办? 这将是一个有效的赋值,但添加SubAble会污染集合。
如何将元素添加到通配符通用集合中?