Java explicit fill of nested maps
Why does it work?
import java.util.HashMap;
import java.util.Map;
public class TestMap {
public static void main(String[] args) {
Map<String, Map<String, Map<String, Map<String,Integer>>>> resultMap = new HashMap<>();
Map<String, Object> aMap = new HashMap<String, Object>();
Map<String, Integer> hiddenMap = new HashMap<String, Integer>();
hiddenMap.put("fortytwo", 42);
aMap.put("key", hiddenMap);
resultMap = (Map<String, Map<String, Map<String, Map<String, Integer>>>>) aMap.get("key");
System.out.println(resultMap);
}
}
also:
Map<String, Map<String, Map<String, Map<String,Map<String,Integer>>>>> resultMap = new HashMap<>();
...
resultMap = (Map<String, Map<String, Map<String, Map<String,Map<String,Integer>>>>>) aMap.get("key");
etc.
How is that hidden card that Map<String, Integer>
is successfully applied to Map<String, Map<String, Map<String, Map<String,Integer>>>> resultMap
?
Always prints:
{fortytwo=42}
Also it works (Map instead of Map):
public static void main(String[] args) {
Map<String, Map<String, Map<String, Map<String,Map<String,Integer>>>>> resultMap = new HashMap<>();
Map<String, Map> aMap = new HashMap<String, Map>();
Map<String, Integer> hiddenMap = new HashMap<String, Integer>();
hiddenMap.put("fortytwo", 42);
aMap.put("key", hiddenMap);
resultMap = (Map<String, Map<String, Map<String, Map<String,Map<String,Integer>>>>>) aMap.get("key");
System.out.println(resultMap);
}
EDIT: Since @shizhz says it is because of Type Erasure of course! Therefore, the above code is equivalent to:
Map resultMap = new HashMap();
Map aMap = new HashMap();
Map hiddenMap = new HashMap();
hiddenMap.put("fortytwo", 42);
aMap.put("key", hiddenMap);
resultMap = (Map) aMap.get("key");
What also works
source to share
Since java jocks are used at compile time to allow for stricter type checking, the type parameter is removed by the compiler according to the Type Erasure rules :
- Replace all type parameters in generic types with your bounds or object if type parameters are not constrained. Thus, the resulting bytecode contains only regular classes, interfaces, and methods.
- Insert type as needed to maintain type safety.
- Create bridging methods to preserve polymorphism on extended generic types.
In the code, the Map<String, Map> aMap = new HashMap<String, Map>();
value in aMap
is a raw type Map
, which means that the compiler has no idea what type it contains, when you try to apply a raw type Map
to any generics Map
like Map<String, Integer>
, the best compiler can do that to give you a warning. The generic type is erased at compile time and the cast type is generated when the value is fetched from the generic map, so you can get a runtime exception ClassCastException
if the type does not match .
Let's take a look at the following example:
public static void main(String[] args) {
Map map = new HashMap();
map.put("hello", "world");
map.put(new Integer(1), 1);
map.put(new Object(), Lists.newArrayList("hello"));
Map<String, Integer> m = (Map<String, Integer>) map;
System.out.println(m);
Integer i = m.get("hello");// ClassCastException happens at here at runtime
}
I am trying to convert Map
containing all key and value types to Map<String, Integer>
, but no compilation error occurs on erasure error, the above code is actually equivalent to:
public static void main(String[] args) {
Map map = new HashMap();
map.put("hello", "world");
map.put(new Integer(1), 1);
map.put(new Object(), Lists.newArrayList("hello"));
Map m = (Map) map;
System.out.println(m);
Integer i = (Integer)m.get("hello");
}
Now you can easily see why the last line called ClassCastException
.
source to share
Since you declared aMap
as Map<String, Object>
, the compiler cannot tell if the values will actually be of type Map<String, Map<String, Map<String,Integer>>>
. It will simply give you an Unchecked Throw warning so you can think about the consequences.
Cast works unless you are actually trying to do something with the values:
resultMap.get("fortytwo").isEmpty();
will lead to
Exception in thread "main" java.lang.ClassCastException:
java.lang.Integer cannot be cast to java.util.Map
If you declared aMap
how Map<String, Map<String, Map<String, Map<String, Map<String, Integer>>>>>
, you could not fit into it hiddenMap
.
source to share