我有以下课程:
interface Able{/* ... */}
class A implements Able{/* ... */}
我有
Map<String,? extends Able> as;
as = new HashMap<String, A>();
为什么以下会导致错误:
as.put("a", new A());
有什么想法吗?
答案 0 :(得分:17)
对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
请注意,继承是一种超级联系(泛化/专业化),而集合定义了一个meronimic关系(容器/容器)。并且将它们两者正式结合起来是一件令人头疼的问题,尽管它很容易被人类模糊的生物使用,例如在法语词汇中synecdocque。 :)
答案 1 :(得分:8)
来自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
。
答案 2 :(得分:3)
了解问题的一个好方法是阅读通配符的含义:
Map<String,? extends Able> as;
“具有String类型的键和一种类型的值的地图,可以扩展Able。”
不允许添加操作的原因是因为它们“打开门”在集合中引入不同类型,这会与打字系统冲突。 e.g。
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
答案 3 :(得分:1)
宣言
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() );
如果它是正确的,你将完成HashMap的内容{“key”,new A()} - 这是类型错误!
答案 4 :(得分:0)
您不能在使用通配符'?'声明的集合中插入任何类型的任何对象
你只能插入“null”
一旦将集合声明为List,编译器就无法知道添加SubAble是安全的。
如果已将Collection<SubSubAble>
分配给Collection<Able>
,该怎么办?这将是一个有效的赋值,但添加SubAble会污染集合。
答案 5 :(得分:0)
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;
}
(再次使用约束,我们无法将新的键/值对添加到此方法返回的地图中)。