Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ClassCastException in Java 11 but not in Java 8 when using HashMap?

Please take a look at my code:

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());

In Java_1.8_60 (Compile & Run) this code is running fine, but when compiled and run in Java 11 it is throwing the following exception:

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)

Are there any changes in Java 11 regarding HashMap?

like image 615
RutujaG Avatar asked Mar 17 '19 07:03

RutujaG


People also ask

How do I resolve Java Lang ClassCastException error?

// type cast an parent type to its child type. In order to deal with ClassCastException be careful that when you're trying to typecast an object of a class into another class ensure that the new type belongs to one of its parent classes or do not try to typecast a parent object to its child type.

What code will cause a ClassCastException to be thrown?

Class ClassCastException Thrown to indicate that the code has attempted to cast an object to a subclass of which it is not an instance. For example, the following code generates a ClassCastException : Object x = new Integer(0); System.

What causes ClassCastException in Java?

ClassCastException is a runtime exception raised in Java when we try to improperly cast a class from one type to another. It's thrown to indicate that the code has attempted to cast an object to a related class, but of which it is not an instance.

Is ClassCastException a checked exception?

ClassCastException is one of the unchecked exception in Java. It can occur in our program when we tried to convert an object of one class type into an object of another class type.


1 Answers

The ClassCastException being thrown is correct. Not having it thrown was caused by a bug in javac, which was fixed in JDK 9 by JDK-8058199. Your code is technically relying on heap pollution not being picked up, so it was never guaranteed to not break.

Basically, in Java 11 (but starting from 9), an extra cast is inserted after getting the value for "LongNumber" from the map on the second to last line. This:

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

Is compiled as:

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

When compiling your code with javac 1.8.0_162, the bytecode for the second to last line is:

 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;

Notice that there is no checkcast instruction after 120:. However, when using 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;

Notice that there is a checkcast instruction at 125:.

This instruction makes the difference, as it basically does an extra type check after getting the value from the versionDoc map. Basically doing this:

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

In Java 11 (starting from 9).


As noted in the comments; the type of the value for "LongNumber" is Integer, which is inside a Map<String, String> due to the unchecked cast a few lines earlier:

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

Where you indirectly cast a Map<String, Object> to a Map<String, String>, even though one of the values is an Integer. The difference is only that there's an extra cast to check the type after getting the value from the map.

Note that the missing checkcast was a bug in javac, so compiling with a different compiler, or different versions of javac could result in different behavior.

like image 127
Jorn Vernee Avatar answered Oct 23 '22 04:10

Jorn Vernee