在我看来它应该有效,但事实并非如此.为什么?源代码:
package javaapplication1; import java.util.*; class A { public static <K,V> Map<K,V> map() { return new HashMap<K,V>(); } } class Person {} class Dog {} public class JavaApplication1 { static void f(Map<Person,List<? extends Dog>> peopleDogList) {} public static void main(String[] args) { f(A.<Person,List<Dog>>map()); } }
非常简单的代码.编译错误:
类JavaApplication1中的方法f不能应用于给出类型;
必需:地图<人员,列表<?延伸狗>
发现:Map< Person,List< Dog>>
原因:实际参数Map< Person,List< Dog>>无法转换为Map< Person,List<?延伸狗>通过方法调用转换.
地图<人员,名单<?延伸狗>更一般,所以编译器应该能够转换?
这也是:地图< Person,List<?延伸狗>> peopleDogList = A.< Person,List< Dog>> map();不起作用. ?延伸狗是指继承狗或狗的对象,所以单词Dog应该没问题?
解决方法
Map< Person,List< Dog>>与Map< Person,List<?不兼容延伸狗>>.在这种情况下,地图的值类型应为List<?扩展Dog>,而不是可转换为相同的东西.但如果您使用Map< Person,?扩展列表<?延伸狗>>对于f的参数,它会起作用.
这是一个涉及更多基本类型的简单示例:
Map<String,List<?>> foo = new HashMap<String,List<Object>>(); // error Map<String,? extends List<?>> foo = new HashMap<String,List<Object>>(); // ok
OP询问为什么会发生这种行为.简单的答案是类型参数是不变的,而不是协变的.也就是说,给定Map< String,List<?>>的类型,地图的值类型必须完全是List<?>,而不是类似的东西.为什么?想象一下,如果允许协变类型:
Map<String,List<A>> foo = new HashMap<>(); Map<String,List<?>> bar = foo; // Disallowed,but let's suppose it's allowed List<B> baz = new ArrayList<>(); baz.add(new B()); bar.put("baz",baz); foo.get("baz").get(0); // Oops,this is actually a B,not an A
糟糕,期望foo.get(“baz”).get(0)成为A被违反.
现在,假设我们以正确的方式做到这一点:
Map<String,? extends List<?>> bar = foo; List<B> baz = new ArrayList<>(); baz.add(new B()); bar.put("baz",baz); // Disallowed
在那里,编译器捕获了尝试将不兼容的列表放入foo(通过别名栏).这是为什么?延伸是必需的.