ClassCastException в Java 11, но не в Java 8 при использовании HashMap? - PullRequest
6 голосов
/ 17 марта 2019

Пожалуйста, посмотрите на мой код:

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 (Compile & Run) этот код работает нормально, но при компиляции и запуске в 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?

1 Ответ

12 голосов
/ 17 марта 2019

Бросок ClassCastException правильный. Отсутствие этого было вызвано ошибкой в ​​javac, которая была исправлена ​​в JDK 9 с помощью JDK-8058199 . Ваш код технически полагается на то, что загрязнение кучи не выявляется, поэтому он никогда не гарантированно не сломался.

По сути, в Java 11 (но начиная с 9) добавляется дополнительное приведение после получения значения для "LongNumber" из карты от второй до последней строки. Это:

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;

Обратите внимание, что после 120: нет инструкции checkcast. Однако при использовании 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;

Обратите внимание, что в 125:.

есть инструкция checkcast.

Эта инструкция имеет значение, так как она в основном выполняет дополнительную проверку типов после получения значения из карты versionDoc. В основном это делается:

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. Разница лишь в том, что есть дополнительный тип для проверки типа после получения значения с карты.

Обратите внимание, что отсутствующий checkcast был ошибкой в ​​javac, поэтому компиляция с использованием другого компилятора или разных версий javac может привести к другому поведению.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...