使用HashMap时,Java 11中的ClassCastException但不是Java 8中的ClassCastException?

Rut*_*jaG 7 java generics hashmap java-8 java-11

请看一下我的代码:

Object longL = 2548214;
Map<String, Object> map = new HashMap<String, Object>(1);
map.put("LongNumber", longL);
List<Map<String, Object>> returnlist = new ArrayList(10);
returnlist.add(map);

List<Object> versionMap1 = new ArrayList(10);
versionMap1.add(returnlist);

List<Map<String, String>> docIdVersionNameMap = new ArrayList<>();
docIdVersionNameMap.addAll((List<Map<String, String>>)versionMap1.get(0));

Map<String, String> versionDoc=docIdVersionNameMap.get(0);

Map<String,String> versionDocInfo=new HashMap<String,String>(1);
versionDocInfo.put(versionDoc.get("LongNumber"),"abc");
System.out.println(versionDocInfo.toString());
Run Code Online (Sandbox Code Playgroud)

在Java_1.8_60(编译并运行)中,此代码运行良好,但是在Java 11中编译并运行时,它将引发以下异常:

Exception in thread "main" java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String (java.lang.Integer and java.lang.String are in module java.base of l
oader 'bootstrap')
        at teststringandlong.Trial.main(Trial.java:35)
Run Code Online (Sandbox Code Playgroud)

Java 11中有关HashMap的变化吗?

Jor*_*nee 12

ClassCastException被抛出是正确的。JDK-8058199javac在JDK 9中修复了一个错误,该错误未引起它被抛出。从技术上讲,您的代码依赖于不清除堆污染,因此永远不能保证它不会损坏。

基本上,在Java 11中(但从9开始),"LongNumber"在从第二行到最后一行的map 获取值之后,插入了额外的强制转换。这个:

versionDocInfo.put(versionDoc.get("LongNumber"),"abc");
Run Code Online (Sandbox Code Playgroud)

编译为:

versionDocInfo.put((String) versionDoc.get("LongNumber"),"abc");
Run Code Online (Sandbox Code Playgroud)

使用编译代码时javac 1.8.0_162,倒数第二行的字节码为:

 114: aload         7
 116: aload         6
 118: ldc           #6                  // String LongNumber
 120: invokeinterface #16,  2           // InterfaceMethod java/util/Map.get:(Ljava/lang/Object;)Ljava/lang/Object;
 125: ldc           #17                 // String abc
 127: invokeinterface #7,  3            // InterfaceMethod java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
Run Code Online (Sandbox Code Playgroud)

请注意,checkcast之后没有任何指令120:。但是,使用时javac 9.0.4

 114: aload         7
 116: aload         6
 118: ldc           #6                  // String LongNumber
 120: invokeinterface #16,  2           // InterfaceMethod java/util/Map.get:(Ljava/lang/Object;)Ljava/lang/Object;
 125: checkcast     #17                 // class java/lang/String
 128: ldc           #18                 // String abc
 130: invokeinterface #7,  3            // InterfaceMethod java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
Run Code Online (Sandbox Code Playgroud)

请注意,这里有一条checkcast说明125:

该指令有所不同,因为它基本上是在从versionDoc映射中获取值之后进行额外的类型检查。基本上这样做:

versionDocInfo.put((String) versionDoc.get("LongNumber"),"abc");
Run Code Online (Sandbox Code Playgroud)

在Java 11中(从9开始)。


如评论中所述;的值类型"LongNumber"Integer,这是在a内的,这是Map<String, String>因为之前未选中的几行强制转换:

docIdVersionNameMap.addAll((List<Map<String, String>>) versionMap1.get(0));
Run Code Online (Sandbox Code Playgroud)

即使其中一个值是,您也可以将a间接转换Map<String, Object>为a 。区别仅在于,从地图获取值后,需要进行额外的强制转换来检查类型。Map<String, String>Integer

请注意,缺少的checkcast是中的错误javac,因此使用其他编译器或不同版本进行编译javac可能会导致不同的行为。

  • @YassinHajaj我不确定jdoodle.com,使用的Java 10版本与此相同,但是正在抛出CCE。必须与它们的设置有关...使用例如var x = 10进行测试,这无法在jdoodle.com上进行编译,因此我想他们正在使用JDK 8中的Javac进行编译,然后与Java 10一起运行。 (2认同)