【发布时间】:2011-06-28 09:15:44
【问题描述】:
我有以下课程:
interface Able{/* ... */}
class A implements Able{/* ... */}
我有
Map<String,? extends Able> as;
as = new HashMap<String, A>();
为什么会出现以下错误:
as.put("a", new A());
有什么想法吗?
【问题讨论】:
我有以下课程:
interface Able{/* ... */}
class A implements Able{/* ... */}
我有
Map<String,? extends Able> as;
as = new HashMap<String, A>();
为什么会出现以下错误:
as.put("a", new A());
有什么想法吗?
【问题讨论】:
对java泛型的引用很好(jdk site)。
确实@Oli_Charlesworth 给出了一个很好的答案,但也许这个会更完整。
在Collection<? extends Able> 中不能插入任何正确的内容。
如果你有
class A implements Able {...}
和
class B implement Able {...}
那么,Collection<? extends Able> 是两者的超类型:
Collection<A>
Collection<B>
因此写一些像
这样的语句是合法的//Code snippet 01
Collection< ? extends Able > list;
Collection<A> listA;
Collection<B> listB;
list = listA;
list = listB;
这确实是通配符 Collection<? extends Able> 存在的原因。
但是,这里的事情变得越来越有趣了:
在Collection<A> 中,您只能插入A 的对象(包括子类)。 Collection<B> 也一样。在这两者中,您都不能添加只是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<A>
list.add( new B() ); //not allowed, will work only if list is List<B>
所以是的,超级类型Collection< ? extends Able > 不接受添加任何内容。更通用的类型提供了子类型功能的交集,因此,子类型的功能更少。在这里,我们失去了添加A 对象和B 对象的能力。这些功能将在层次结构中稍后发生......甚至意味着我们不能在超类中添加任何东西Collection< ? extends Able >
补充说明:
另外,请注意,在 Collection<Able> 中,您可以像这样添加任何您想要的内容:
Collection< Able > list;
list.add( new A() ); //valid
list.add( new B() ); //valid
但是,Collection<Able> 不是Collection<A> 和Collection<B> 的超类。这意味着,与任何继承关系一样,子类可以做它们的超类可以做的任何事情,因为继承是专门化的。因此,这意味着我们可以将 A 对象和 B 对象添加到子类 Collection<A> 和 Collection<B> 中,但事实并非如此。所以它不是你不能拥有的超类:
Collection<Able> list;
Collection<A> listA;
Collection<B> listB;
list = listA; //not valid because there is no inheritance hierarchy
list = listB; //not valid because there is no inheritance hierarchy
请注意,继承是一种超音序关系(泛化/专业化),而集合定义了一种美音关系(容器/容器)。将它们正式结合起来是一件令人头疼的事情,尽管人类的模糊生物在某种程度上很容易使用它,例如在法语修辞格中: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-- 一个Shape的未知子类型。因为我们 不知道它是什么类型,我们不知道 知道它是否是一个超类型Rectangle;它可能是也可能不是 这样的超类型,所以它是不安全的 在那里传递Rectangle。
【讨论】:
理解这个问题的一个好方法是阅读通配符的含义:
Map<String,? extends Able> as;
“具有 String 类型键和 一种扩展 Able 的类型 值的映射。”
之所以不允许添加操作,是因为它们“开门”在集合中引入不同的类型,这会与类型系统冲突。 例如
class UnAble implements Able;
Map<String,UnAble> unableMap = new HashMap<String,UnAble>();
Map<String,? extends Able> ableMap = unableMap;
ableMap.put("wontwork",new A()); // type mismatch: insert an A-type into an Unable map
通配符结构的正确用法是:
Result processAble(Map<String,? extends Able>) { ... read records & do something ... }
Map<String,A> ableMap = new HashMap<String,A>;
ableMap.put("willwork",new A());
processAble(as);
processAble(unableMap); // from the definition above
【讨论】:
声明
Map<String,? extends Able> as;
表示“任何映射,具有字符串键,值是 Able 的子类型”。因此,例如,您可以执行以下操作:
Map<String,? extends Able> as = new HashMap<String, SubSubAble>();
现在让我们看看这个例子:
Map<String,? extends Able> as = new HashMap<String, SubSubAble>();
as.put("key", new A() );
如果它是正确的,您将完成具有内容 {"key", new A()} 的 HashMap - 这是类型错误!
【讨论】:
Collection<?> 是各种集合的超类型。它不是可以容纳任何类型的一个集合。至少那是我对整个概念的误解。
我们可以在不关心泛型类型的地方使用它,就像这个例子:
public static void print(Collection<?> aCollection) {
for (Object o:aCollection) {
System.out.println(o);
}
}
如果我们选择了签名:
public static void print(Collection<Object> aCollection)
我们会将自己限制为 Collection<Object> 类型的集合 - 换句话说,这样的方法不会接受 Collection<String> 类型的值。
所以Collection<?> 类型不是可以采用任何类型的集合。它只需要未知类型。而且由于我们不知道那个类型(它的未知;)),我们永远不能添加一个值,因为在 java 中没有一个类型是未知类型的子类。
如果我们添加边界(如<? extends Able>),则类型仍然未知。
您正在寻找地图的声明,其值都实现了Able 接口。正确的声明很简单:
Map<String, Able> map;
假设我们有两种类型 A 和 B 子类 Able 和另外两个映射
Map<String, A> aMap;
Map<String, B> bMap;
并且想要一个返回任何映射的方法,其值实现了Able 接口:那么我们使用通配符:
public Map<String, ? extends Able> createAorBMap(boolean flag) {
return flag ? aMap: bMap;
}
(同样有约束,我们不能将新的键/值对添加到此方法返回的映射中)。
【讨论】:
您不能在使用通配符“?”声明的集合中插入任何类型的对象。
你只能插入“null”
一旦将集合声明为 List,编译器就无法知道添加 SubAble 是否安全。
如果 Collection<SubSubAble> 已分配给 Collection<Able> 会怎样?这将是一个有效的分配,但添加一个 SubAble 会污染集合。
【讨论】: