继承不适用于作为泛型类型传递

时间:2013-11-14 11:33:56

标签: java oop generics

考虑我的自定义扩展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());

在初始化时没有指定泛型似乎很奇怪,但它没有。

3 个答案:

答案 0 :(得分:7)

此声明无效

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

因为customs的类型为Map<String, HashMap<String, Object>>,而您指定的LinkedHashMap类型为<String, CustomHashMap>,其中CustomHashMapHashMap<String, Object>的子类1}}。

泛型为invariant:对于任何两种不同类型T1T2HashMap<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链接可以帮助您学习泛型和子类型