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

+3


source to share


2 answers


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

.

+4


source


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

.

+3


source







All Articles