It is important to understand the implication of the wildcard types.
You already understood that you can assign your Map<Integer, Map<Integer, String>>
to Map<?,??>
as Map<?, ?>
implies arbitrary types, unknown to whoever might have a reference of the declared type Map<?, ?>
. So you can assign any map to Map<?, ?>
.
In contrast, if you have a Map<?, Map<?, ?>>
it has an unknown key type but the value type is not unknown. It’s Map<?,?>
the type, recall the information above, that can be assigned with any map.
So, the following code is legal:
Map<?, Map<?, ?>> map=new HashMap<>();
map.put(null, Collections.<String,String>singletonMap("foo", "bar"));
map.put(null, Collections.<Double,Integer>singletonMap(42.0, 1000));
map.put(null, Collections.<Object,Boolean>singletonMap(false, true));
Here, we are putting a null
key as we can’t put
anything else for keys but arbitrary typed maps as values as that’s what a value type of Map<?, ?>
implies: can be assigned from arbitrary maps. Note that by iterating over the entries we can also set other entries having non-null
keys to arbitrary maps then.
So I’m quite sure that you don’t want to assign your Map<Integer, Map<Integer, String>>
to a Map<?, Map<?, ?>>
and discover arbitrary maps not being Map<Integer, String>
as values afterwards and that you are quite happy that the compiler doesn’t allow this.
What you actually want to do is to assign your map to a type which has both, key and value type, unknown but still telling that your values are maps:
Map<Integer, Map<Integer, String>> someMap = new HashMap<>();
Map<?, ? extends Map<?, ?>> map=someMap;
In the generic type system Map<Integer, String>
is a sub-type of Map<?, ?>
so you can assign it to Map<?, ?>
as well as ? extends Map<?, ?>
. This sub-type relationship is not different than the relationship of String
to Object
. You can assign any String
to a variable of type Object
but if you have a Map<?,String>
you can’t assign it to Map<?,Object>
but only to Map<?, ? extends Object>
for the same reason: the map shall continue to contain String
s as values rather than receiving arbitrary objects.
Note that you can workaround this limitation. You can say:
Map<Integer, Map<Integer, String>> someMap = new HashMap<>();
Map<?, Map<?, ?>> map=Collections.unmodifiableMap(someMap);
Since the map returned by unmodifiableMap
does not allow any modifications, it allows widening the key and value types. The contained values are of the specified type (i.e. Map<?, ?>
) when you query the map, but attempts to put in arbitrary map values, while not rejected by the compiler, will be rejected at runtime.