Java,泛型不起作用

时间:2013-08-20 23:37:42

标签: java generics

在我看来它应该有效,但事实并非如此。为什么?源代码:

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表示继承DogDog的对象,因此单词Dog应该没问题?

2 个答案:

答案 0 :(得分:13)

Map<Person, List<Dog>>Map<Person, List<? extends Dog>>不兼容。在这种情况下,地图的值类型应为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<?>>类型,地图的值类型必须完全 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是必需的原因。

答案 1 :(得分:0)

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

编译器不允许您执行此操作的原因是您调用的方法可能会执行Map<Person, List<? extends Dog>>上允许的操作,但HashMap<Person, List<Dog>>上不允许这样做。例如,它可能会尝试向地图添加条目,其值为ArrayList<Rottweiler>。这适用于Map<Person, List<? extends Dog>>(假设Rottweiler延伸Dog),但不适用于任何Map<Person, List<Dog>>