7

在我看来,它应该工作,但它没有。为什么?源代码:

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表示继承Dogor的对象Dog,所以单词Dog应该可以吗?

4

2 回答 2

14

Map<Person, List<Dog>>不兼容Map<Person, List<? extends Dog>>。在这种情况下,映射的值类型应为List<? extends Dog>,而不是可转换为相同的值。但是如果你使用Map<Person, ? extends List<? extends Dog>>forf的参数,它会起作用。

这是一个涉及更多基本类型的简单示例:

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)对成为 an的期望。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需要。

于 2013-08-20T23:45:00.053 回答
0

问题是您试图将 a 传递HashMap<Person, List<Dog>>给期望 a 的方法Map<Person, List<? extends Dog>>

编译器不允许你这样做的原因是你调用的方法可能会做一些在 a 上允许Map<Person, List<? extends Dog>>但在 a 上不允许的事情HashMap<Person, List<Dog>>。例如,它可能会尝试向地图添加一个条目,其中值为ArrayList<Rottweiler>. 这对于 a Map<Person, List<? extends Dog>>(假设Rottweilerextends Dog)很好,但对于任何类型的Map<Person, List<Dog>>.

于 2013-08-21T00:25:29.470 回答