在我看来它应该有效,但它没有。为什么?源代码:
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<? extends Dog>
找到:Map<Person, List<Dog>>
原因:实参Map<Person, List<Dog>>
无法转换为 Map<Person, List<? extends Dog>
通过方法调用转换。
Map<Person, List<? extends Dog>
比较通用,那么编译器应该可以转换吧?
还有这个:Map<Person, List<? extends Dog>> peopleDogList = A.<Person, List<Dog>>map();
不起作用。 ? extends Dog
表示继承 Dog
的对象或 Dog
, 所以字 Dog
应该可以吧?
最佳答案
Map<Person, List<Dog>>
与 Map<Person, List<? extends Dog>>
不兼容.在这种情况下, map 的值类型应为 List<? extends Dog>
,而不是可以转换为相同的东西。但是如果你使用 Map<Person, ? extends List<? extends Dog>>
对于 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<?>>
, map 的值类型必须完全 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, List<A>> foo = new HashMap<>();
Map<String, ? extends List<?>> bar = foo;
List<B> baz = new ArrayList<>();
baz.add(new B());
bar.put("baz", baz); // Disallowed
在那里,编译器捕获了将不兼容列表放入 foo
的尝试。 (通过别名 bar
)。这就是为什么 ? extends
是必需的。
关于Java,泛型不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18346641/