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?
// 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.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With