I came across an oddity today that I don’t quite understand. Take this code, for example:
Object iMap = new HashMap<Integer, Object>() {{
put(5, "thing1");
put(6, "thing2");
}};
Map<String, Object> sMap = (Map<String, Object>)iMap;
// No error, prints out java.lang.Integer:
System.out.println(new ArrayList(sMap.keySet()).get(0).getClass().getName();
// No error, prints out 5:
Object key = new ArrayList<String>(sMap.keySet()).get(0);
System.out.println(key.toString());
// ClassCastException:
String s = new ArrayList<String>(sMap.keySet()).get(0);
So, what gives? Why can I cast a Map with keys of type Integer to one of type String without any issues? Shouldn’t that throw an error? And why can I even cast to ArrayList<String> and still get no errors? It’s supposedly a list of only Strings, but I can retrieve an Integer from it.
I’m a bit baffled by this, and I’m wondering if anyone here knows enough about the inner workings of these classes to help me out.
You can cast
Map<Integer, Object>toMap<String, Object>“without any issues”… until you try to use it.The problem starts in this line:
where the compiler warns you with this message:
You ignored that warning.
This is all happening because of runtime type erasure – at runtime there are no types, eg you have just
Map, etc. Types are just there at compile to help you not do what you are doing here.The reason this line explodes:
is that the sMap actually refers to the Map that had Integers for keys in its entries. When you actually went to pull one of the keys out, it was an Integer which java then tries to assign to a String… boom!
btw, this part doesn’t compile:
you would need to cast iMap to
Map<Integer, Object>like this: