java - 使用 HashMap 时 Java 11 中的 ClassCastException 而不是 Java 8 中的?

标签 java generics java-8 hashmap 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());

在 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)

Java 11 中关于 HashMap 有什么变化吗?

最佳答案

ClassCastException被扔是正确的。没有抛出它是由 javac 中的错误引起的,已在 JDK 9 中由 JDK-8058199 修复.您的代码在技术上依赖于未被拾取的堆污染,因此无法保证不会中断。

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

versionDocInfo.put(versionDoc.get("LongNumber"),"abc");

编译为:

versionDocInfo.put((String) versionDoc.get("LongNumber"),"abc");

使用 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;

注意没有 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;

注意有一个 checkcast指令在 125: .

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

versionDocInfo.put((String) versionDoc.get("LongNumber"),"abc");

在 Java 11(从 9 开始)中。


如评论中所述; "LongNumber" 的值类型是Integer , 在 Map<String, String> 内由于前面几行未经检查的转换:

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

间接转换 Map<String, Object> 的地方到 Map<String, String> ,即使其中一个值是 Integer .不同之处仅在于在从 map 获取值后有一个额外的转换来检查类型。

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

关于java - 使用 HashMap 时 Java 11 中的 ClassCastException 而不是 Java 8 中的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55204967/

相关文章:

java - 拉撒路或Java

java - grails urlMappings dsl

c# - C# 编译器如何使用泛型?

java - 一列添加两个按钮并获取点击行值javafx

java - 如何使用方法引用调用参数化方法

Java 8 替代 Map.entry() 将值映射到新 Map?

java - 使用 hibernate 在 hsqldb 中选择更新

java - 我的 Android 应用程序如何与 native 守护进程通信?

java泛型运行时类型

Java 泛型、不可转换类型、类型转换、堆 d-ary