Java嵌套映射的重复投射

时间:2017-04-14 13:04:37

标签: java collections casting

为什么这个演员有效?

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

public class TestMap {
    public static void main(String[] args) {
        Map<String, Map<String, Map<String, Map<String,Integer>>>> resultMap = new HashMap<>();
        Map<String, Object> aMap = new HashMap<String, Object>();
        Map<String, Integer> hiddenMap = new HashMap<String, Integer>();
        hiddenMap.put("fortytwo", 42);
        aMap.put("key", hiddenMap);
        resultMap =  (Map<String, Map<String, Map<String, Map<String, Integer>>>>) aMap.get("key");
        System.out.println(resultMap);
    }
}

也是这样:

Map<String, Map<String, Map<String, Map<String,Map<String,Integer>>>>> resultMap = new HashMap<>();
...
resultMap =  (Map<String, Map<String, Map<String, Map<String,Map<String,Integer>>>>>) aMap.get("key");

依旧......

如果Map<String, Integer>隐藏地图成功投放到Map<String, Map<String, Map<String, Map<String,Integer>>>> resultMap,会如何发生这种情况?

始终打印:

{fortytwo=42}

这也适用(地图而不是地图):

public static void main(String[] args) {

        Map<String, Map<String, Map<String, Map<String,Map<String,Integer>>>>> resultMap = new HashMap<>();
        Map<String, Map> aMap = new HashMap<String, Map>();
        Map<String, Integer> hiddenMap = new HashMap<String, Integer>();
        hiddenMap.put("fortytwo", 42);
        aMap.put("key", hiddenMap);
        resultMap =  (Map<String, Map<String, Map<String, Map<String,Map<String,Integer>>>>>) aMap.get("key");
        System.out.println(resultMap);

    }
编辑:正如@shizhz所说,这是因为类型擦除当然!所以上面的代码相当于:

Map resultMap = new HashMap();
Map aMap = new HashMap();
Map hiddenMap = new HashMap();
hiddenMap.put("fortytwo", 42);
aMap.put("key", hiddenMap);
resultMap = (Map) aMap.get("key");

哪个也有效

2 个答案:

答案 0 :(得分:4)

因为在编译时使用java泛型来提供更严格的类型检查,所以编译器会根据Type Erasure rules删除type参数:

  • 如果类型参数是无界的,则将泛型类型中的所有类型参数替换为其边界或对象。因此,生成的字节码只包含普通的类,接口和方法。
  • 如有必要,插入类型转换以保护类型安全。
  • 生成桥接方法以保留扩展泛型类型中的多态性。

在代码Map<String, Map> aMap = new HashMap<String, Map>();中,aMap中的值是原始类型Map,这意味着当您尝试使用时,编译器不知道它包含的类型是什么将原始类型的Map转换为Map的任何泛型类型,如Map<String, Integer>,最好的编译器可以做的就是给你一个警告。泛型类型在编译时被擦除,并且当您从通用映射获取值时将生成类型转换,因此如果类型不匹配,您只能获得运行时 ClassCastException异常。

让我们看看下面的例子:

public static void main(String[] args) {
    Map map = new HashMap();

    map.put("hello", "world");
    map.put(new Integer(1), 1);
    map.put(new Object(), Lists.newArrayList("hello"));

    Map<String, Integer> m =  (Map<String, Integer>) map;
    System.out.println(m);

    Integer i = m.get("hello");// ClassCastException happens at here at runtime
}

我尝试将包含各种键和值的Map转换为Map<String, Integer>但是没有编译错误,在类型擦除后,上面的代码实际上是等效的到:

public static void main(String[] args) {
    Map map = new HashMap();

    map.put("hello", "world");
    map.put(new Integer(1), 1);
    map.put(new Object(), Lists.newArrayList("hello"));

    Map m = (Map) map;
    System.out.println(m);

    Integer i = (Integer)m.get("hello");
}

现在,您可以轻松地说明为什么最后一行导致了ClassCastException

答案 1 :(得分:3)

由于您已将aMap声明为Map<String, Object>,因此编译器无法判断这些值是否确实属于Map<String, Map<String, Map<String,Integer>>>类型。它会给你一个“未经检查的演员”警告,让你考虑后果。

除非你真的试图用值来做某事,否则演员会工作:

resultMap.get("fortytwo").isEmpty();

将导致

Exception in thread "main" java.lang.ClassCastException:
  java.lang.Integer cannot be cast to java.util.Map

如果您已将aMap声明为Map<String, Map<String, Map<String, Map<String, Map<String, Integer>>>>>,则您首先无法将hiddenMap放入其中。