考虑我的自定义扩展hashmap:
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();
在将{Custom(简称)地图添加(放入)customs
地图时,它也有效。
customs.put("test", new CustomHashMap());
在初始化时没有指定泛型似乎很奇怪,但它没有。
答案 0 :(得分:7)
此声明无效
Map<String, HashMap<String, Object>> customs = new LinkedHashMap<String, CustomHashMap>();
因为customs
的类型为Map<String, HashMap<String, Object>>
,而您指定的LinkedHashMap
类型为<String, CustomHashMap>
,其中CustomHashMap
是HashMap<String, Object>
的子类1}}。
泛型为invariant
:对于任何两种不同类型T1
和T2
,HashMap<String, T1>
既不是子类型也不是HashMap<String, T2>
的超类型。因此,无法将LinkedHashMap<String, CustomHashMap>
分配给Map<String, HashMap<String, Object>>
。另一方面,数组是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很好地解释了这种方法的一些额外信息,这是公认的答案。
答案 1 :(得分:4)
使用泛型时,应始终牢记类型擦除。在运行时,类型Map
的对象不再知道其类型参数。结果是:LinkedHashMap<String, CustomHashMap>
不是Map<String, HashMap<String, Object>>
的子类型。
如果你想让某种子类型相关,你必须按照以下方式进行:
Map<String, ? extends HashMap<String, Object>> customs = new LinkedHashMap<String, CustomHashMap>();
这被称为上限通配符,并且完全适用于该情况:获得子类型关系。有关详细信息,请参阅Java tutorial about generics。
根据评论的其他信息:
上限版本在如何使用海关地图方面存在缺点。您不能再将实例放入该地图中。唯一允许的值是null
。原因是,您可以让另一个类扩展Map<String, HashMap>
并尝试将其实例放入您的海关地图中。但这是一个问题,因为变量海关是指用CustomHashMap
参数化的地图。
使用有界通配符时,应始终提醒 PECS 。 PECS代表“生产者延伸,消费者超级”。这对于方法参数很有价值。如果您编写的方法只需要从这样的地图中读取值,则可以将参数键入为Map<String, ? extends Map<String, Object>>
。这被称为生产者。如果您只需要写入该地图,请使用关键字 super 。如果您同时需要 - 读取和写入 - 您也无法做到。
答案 2 :(得分:2)
来自oracle网站上的java教程
List<String> ls = new ArrayList<String>(); // 1
List<Object> lo = ls; // 2
第1行肯定是合法的。问题的棘手部分是第2行。这归结为一个问题:是一个String列表对象列表。大多数人本能地回答:“当然!”
好吧,看看接下来几行:
lo.add(new Object()); // 3
String s = ls.get(0); // 4: Attempts to assign an Object to a String!
这里我们别名ls和lo。通过别名lo访问ls,一个String列表,我们可以在其中插入任意对象。因此,ls不再仅仅支持Strings,当我们尝试从中获取某些东西时,我们会得到一个粗鲁的惊喜。
Java编译器当然会阻止这种情况发生。第2行将导致编译时错误。
this链接可以帮助您学习泛型和子类型