8

考虑我的自定义扩展哈希图:

public class CustomHashMap extends HashMap<String, Object> {
...
}

为什么这不起作用,因为 CustomHashMap 是 HashMap 的孩子?

Map<String, HashMap<String, Object>> customs = new LinkedHashMap<String, CustomHashMap>();

但这有效:

Map<String, HashMap<String, Object>> customs = new LinkedHashMap();

并且在将 CustomHashMap 添加(放入)到customsMap 时也可以使用。

customs.put("test", new CustomHashMap());

在初始化时不指定泛型似乎很奇怪,但否则不会。

4

3 回答 3

7

这个说法不成立

Map<String, HashMap<String, Object>> customs = new LinkedHashMap<String, CustomHashMap>();

因为customsis 的类型Map<String, HashMap<String, Object>>,你正在分配一个LinkedHashMap类型<String, CustomHashMap>,其中CustomHashMap是 的子类HashMap<String, Object>

泛型是invariant: 对于任何两种不同的类型T1,并且T2,HashMap<String, T1>既不是 的子类型也不是超类型HashMap<String, T2>。所以,LinkedHashMap<String, CustomHashMap>不能分配给Map<String, HashMap<String, Object>>. 另一方面,数组 are covariant,这意味着下面的语句将编译而没有任何错误或警告。但是,如果您将任何其他子类型HashMap<String, Object>放入其中,它可能会在运行时失败(这可能会造成更大的伤害) CustomHashMap

HashMap<String, Object>[] mapArray = new CustomHashMap[1];
mapArray[0] = new CustomHashMap_1();// this will throw java.lang.ArrayStoreException

现在,如果要分配LinkedHashMap<String, CustomHashMap>Map<String, HashMap<String, Object>>,请将语句更改为:

Map<String, ? extends HashMap<String, Object>> customs = new LinkedHashMap<String, CustomHashMap>();

@Seelenvirtuose 很好地解释了有关此方法的一些附加信息,这是公认的答案。

于 2013-11-14T11:44:25.203 回答
4

使用泛型时,您应该始终牢记类型擦除。在运行时,类型的对象Map不再知道其类型参数。结果: ALinkedHashMap<String, CustomHashMap>不是 的子类型Map<String, HashMap<String, Object>>

如果你想有一些相关的子类型,你必须按照以下方式进行:

Map<String, ? extends HashMap<String, Object>> customs = new LinkedHashMap<String, CustomHashMap>();

这被称为上限通配符,并且恰好存在于这种情况下:获得子类型关系。有关更多信息,请参阅有关泛型的 Java 教程


根据评论的附加信息:

上限版本在如何使用海关地图上有一个缺点。您不能再将实例放入该地图中。唯一允许的值是null. 原因是,您可以扩展另一个类Map<String, HashMap>并尝试将其实例放入您的海关地图中。但这是一个问题,因为变量 custom 指的是用 参数化的地图CustomHashMap

使用有界通配符时,应始终提醒PECS。PECS 代表“生产者延伸,消费者超”。这对于方法参数很有价值。如果您编写的方法只需要从此类映射中读取值,则可以将参数键入为Map<String, ? extends Map<String, Object>>. 这称为生产者。如果您只需要写入该地图,请使用关键字super。如果你同时需要——读和写——你也不能这样做。

于 2013-11-14T11:48:29.483 回答
2

来自 oracle 网站上的 java 教程

List<String> ls = new ArrayList<String>(); // 1
List<Object> lo = ls; // 2 

第 1 行当然是合法的。问题中比较棘手的部分是第 2 行。这归结为问题:是字符串列表还是对象列表。大多数人本能地回答:“当然!”

好吧,看看接下来的几行:

lo.add(new Object()); // 3
String s = ls.get(0); // 4: Attempts to assign an Object to a String!

在这里,我们给 ls 和 lo 起了别名。通过别名 lo 访问字符串列表 ls,我们可以将任意对象插入其中。结果 ls 不再只包含字符串,当我们尝试从中获取一些东西时,我们会得到一个粗鲁的惊喜。

Java 编译器当然会阻止这种情况发生。第 2 行将导致编译时错误。

链接将帮助您学习泛型和子类型

于 2013-11-14T11:40:15.080 回答