Got a Map<String, ? extends Map<String, Integer>> mapOfMaps
variable.
Map<String, Integer> result = mapOfMaps.get("aaa");
works, but
Map<String, Integer> result = mapOfMaps.getOrDefault("aaa",Collections.emptyMap());
says
The method getOrDefault(Object, capture#1-of ? extends Map<String,Integer>) in the type Map<String,capture#1-of ? extends Map<String,Integer>> is not applicable for the arguments (String, Map<String,Integer>)
the same goes for
Map<String, Integer> result = mapOfMaps.getOrDefault("aaa",Collections.<String,Integer>emptyMap());
or
Map<String, Integer> result = mapOfMaps.getOrDefault("aaa",(Map<String,Integer>)Collections.EMPTY_MAP);
or even
Map<String, Integer> result = mapOfMaps.getOrDefault("aaa",new HashMap<String, Integer>());
Is there a way of using the getOrDefault like that or do I have to use the clunky way ?
Map<String, Integer> result = mapOfMaps.get("aaa");
if( result == null ) {
result = Collections.emptyMap();
}
CodePudding user response:
You can use Collections.unmodifiableMap
to view your map as Map<String, Map<String, Integer>>
.
Map<String, ? extends Map<String, Integer>> mapOfMaps = new HashMap<>();
Map<String, Map<String, Integer>> view = Collections.unmodifiableMap(mapOfMaps);
Map<String, Integer> map = view.getOrDefault("foo", Collections.emptyMap());
In a single line, however, it still looks ugly, since you need to specify the generic type arguments for unmodifiableMap
.
Map<String, Integer> map = Collections.<String, Map<String, Integer>>
unmodifiableMap(mapOfMaps).getOrDefault("foo", Collections.emptyMap());
Explanation
You cannot call any method that has an unbounded or extends
-bounded wildcard parameter, because the exact type of the wildcard is not known at compile time.
Let's make this simpler and look at Map<String, ? extends Number>
, to which you could assign either of
Map<String, ? extends Number> map = new HashMap<String, Integer>();
Map<String, ? extends Number> map = new HashMap<String, Double>();
However, when calling map.getOrDefault(Object k, V defaultValue)
, there is no way to determine the type for defaultValue
at compile time, since the actual type may change at runtime, even for the very same assignment (not the same instance though).
// compile-time error, could require a Double or any other Number-type
Number i = map.getOrDefault("foo", (Number)Integer.MAX_VALUE);
CodePudding user response:
One possible, but still rather clunky, solution is a helper function:
static <K1, K2, V, M extends Map<K2, V>> Map<K2, V> getOrEmpty(Map<K1, M> mapOfMaps, K1 key) {
Map<K2, V> submap = mapOfMaps.get(key);
return submap != null ? submap : Collections.emptyMap();
}
and then call it like
Map<String, Integer> result = getOrEmpty(mapOfMaps,"aaa");
But I would still prefer a solution without having to define an extra function.