当f1
编译时,非常相似的f2
不会,我只是无法解释原因。
(在Intellij 9和Eclipse 3.6上测试)
我真的以为我已经完成了这样的问题。
import java.util.*;
public class Demo {
public List<? extends Set<Integer>> f1(){
final List<HashSet<Integer>> list = null;
return list;
}
public List<List<? extends Set<Integer>>> f2(){
final List<List<HashSet<Integer>>> list = null;
return list;
}
}
答案 0 :(得分:11)
List<List<HashSet<Integer>>>
无法分配给List<List<? extends Set<Integer>>>
,原因相同List<HashSet<Integer>>
无法分配给List<Set<Integer>>
。
你可以通过更改它来编译它:
public List<List<? extends Set<Integer>>> f2(){
进入这个:
public List<? extends List<? extends Set<Integer>>> f2(){
你的代码没有编译的原因,以及为什么我给出的另一个例子(即:“List<HashSet<Integer>>
无法分配给List<Set<Integer>>
”)是Java generics are not covariant。
规范示例是,即使Circle
延伸Shape
,List<Circle>
也不会延伸List<Shape>
。如果确实如此,则List<Circle>
需要add(Shape)
方法接受Square
个对象,但显然您不希望能够将Square
个对象添加到List<Circle>
。
当您使用通配符时,您将获得一个切掉某些方法的类型。 List<? extends Shape>
保留返回E
的方法,但它没有将E
作为参数的任何方法。这意味着您仍然使用E get(int)
方法,但add(E)
已消失。 List<? extends Shape>
是超级类型List<Shape>
以及List<Circle>
,List<? extends Circle>
等。(? super
通配符以另一种方式切片:的方法返回类型参数的值)
你的例子更复杂,因为它有嵌套的类型参数,但归结为同样的事情:
List<HashSet<Integer>>
是List<? extends Set<Integer>>
List<...>
)中会产生一对不再具有子/超类型关系的类型。也就是说,List<List<HashSet<Integer>>>
不是List<List<? extends Set<Integer>>>
的子类型List<...>
换行,而是用List<? extends ...>
换行,最终会保留原始关系。 (这只是一个经验法则,但它可能涵盖了80%你想要使用通配符的情况。)请注意,trashgod和BalusC都是正确的,因为你可能不希望返回这种奇怪的类型。 List<List<Set<Integer>>>
将是一种更常用的返回类型。只要你始终使用集合接口而不是具体的集合类作为类型参数,这应该可以正常工作。例如:您无法将List<ImmutableSet<Integer>>
分配给List<Set<Integer>>
,但您可以将ImmutableSet<Integer>
个实例放入List<Set<Integer>>
,所以永远不要说List<ImmutableSet<Integer>>
,说{ {1}}。
答案 1 :(得分:6)
“不要使用通配符类型作为返回类型。它不会为用户提供额外的灵活性,而是迫使他们在客户端代码中使用通配符类型。” - Joshua Bloch,{{3} },第5章,第28项。
答案 2 :(得分:2)
这太长了,不适合评论。我只想说,以这种方式宣布它是没有意义的。您也可以执行以下操作:
public List<List<Set<Integer>>> f2() {
List<List<Set<Integer>>> list = new ArrayList<List<Set<Integer>>>();
List<Set<Integer>> nestedList = new ArrayList<Set<Integer>>();
list.add(nestedList);
Set<Integer> set = new HashSet<Integer>();
nestedList.add(set);
return list;
}
效果很好。我认为没有必要在这里使用? extends SomeInterface
。
更新:根据评论,您最初想解决以下问题:
public List<Map<Integer, Set<Integer>>> getOutcomes() {
Map<HashSet<Integer>, Integer> map = new HashMap<HashSet<Integer>, Integer>();
List<Map<Integer, Set<Integer>>> outcomes = new ArrayList<Map<Integer, Set<Integer>>>();
for (Map.Entry<HashSet<Integer>, Integer> entry : map.entrySet()) {
outcomes.add(asMap(entry.getValue(), entry.getKey()));
// add() gives compiler error: The method add(Map<Integer,Set<Integer>>)
// in the type List<Map<Integer,Set<Integer>>> is not applicable for
// the arguments (Map<Integer,HashSet<Integer>>)
}
return outcomes;
}
public <K, V> Map<K, V> asMap(K k, V v) {
Map<K, V> result = new HashMap<K, V>();
result.put(k, v);
return result;
}
这可以通过声明接口(在这种情况下为Set
)而不是实现(在这种情况下为HashSet
)作为泛型类型来解决。所以:
public List<Map<Integer, Set<Integer>>> getOutcomes() {
Map<Set<Integer>, Integer> map = new HashMap<Set<Integer>, Integer>();
List<Map<Integer, Set<Integer>>> outcomes = new ArrayList<Map<Integer, Set<Integer>>>();
for (Map.Entry<Set<Integer>, Integer> entry : map.entrySet()) {
outcomes.add(asMap(entry.getValue(), entry.getKey()));
// add() now compiles fine.
}
return outcomes;
}
在未来的问题中,尝试询问如何解决特定问题,而不是如何实现特定解决方案(毕竟这可能不是正确的解决方案)。