Java通用问题

时间:2011-05-12 06:21:36

标签: java generics inheritance types

以下代码编译,但如果我取消注释注释行,它不会,我很困惑为什么。 HashMap确实扩展了AbstractMap,并且声明map的第一行编译正常。

import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Map;

public class Test {

    public static void main(String args[]) {
        Map<String, ? extends AbstractMap<String, String>> map = new HashMap<String, HashMap<String, String>>();
        //map.put("one", new HashMap<String, String>());
    }
}

而且,我知道“正确的方法”是这样的:

import java.util.HashMap;
import java.util.Map;

public class Test {

    public static void main(String args[]) {
        Map<String, Map<String, String>> map = new HashMap<String, Map<String, String>>();
        map.put("one", new HashMap<String, String>());
    }
}

3 个答案:

答案 0 :(得分:7)

第一个代码是不安全的 - 想象一下你真的写了:

HashMap<String, ConcurrentHashMap<String, String>> strongMap = 
    new HashMap<String, ConcurrentHashMap<String, String>>();
Map<String, ? extends AbstractMap<String, String>> map = strongMap;

现在:

map.put("one", new HashMap<String, String>());
ConcurrentHashMap<String, String> x = strongMap.get("one");

我们 应该ConcurrentHashMap - 但实际上我们只有HashMap

如果我们减少正在进行的泛型数量,这实际上要简单得多解释......你的场景真的等同于(比如说):

List<? extends Fruit> list = new List<Apple>();
list.add(new Apple());

看起来没问题,直到你认为它的有效性(就编译器而言)相当于:

List<Apple> apples = new ArrayList<Apple>();
List<? extends Fruit> list = apples;
list.add(new Orange());
Apple apple = list.get(0); // Should be okay... but element 0 is an Orange!

显然没关系。编译器必须以相同的方式处理这两个,因此它们都使它们无效。

答案 1 :(得分:0)

除了Jon的优秀答案外,另请参阅此question on PECS,其中涵盖了很多相同的理由。

答案 2 :(得分:0)

如果您需要真正的技术性解释,我建议您阅读component based software中的这些幻灯片,以获得问题的完整图片,因为您的小问题在很大程度上会产生巨大的影响:)

实际条款是逆变协方差。在these slides中搜索OO设计及其在大型系统中的局限性。

你的问题混合了一些泛型混合在一起:)

即,你的合同(某种东西比水果更具体的果实)指明列表必须能够保持所有种水果,但是你创建一个只能保存某种水果的列表(苹果或某种比苹果更具体的东西),根据我应该引发编译器错误,但是Java编译器太漂亮了泛型未在Java中正确实现(从内部和内省的角度来看)。

容器实例可以是容器类型/类的协变或不变量,但实例的容器必须是容器类型/类的不变量。

一个具体的例子:

List<Fruit> list = new ArrayList<Fruit>();

一般例子:

ConatainerType<ElementOfList> list = new MoreSpecificContainerType<ElementOfList>();

ElementOfList 必须同时满足协方差和逆变,因为对象既可以被放入(协方差)和检索(逆变),也只能保留不变性,即相同的类型/类别。

这是一个非常长的答案,但我希望它可以帮助将来提出类似问题的人。