Java Map.getOrDefault 有界通配符

Java Map.getOrDefault with bounded wildcard

得到一个 Map<String, ? extends Map<String, Integer>> mapOfMaps 变量。

Map<String, Integer> result = mapOfMaps.get("aaa");

有效,但是

Map<String, Integer> result = mapOfMaps.getOrDefault("aaa",Collections.emptyMap());

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>)

同样适用于

Map<String, Integer> result = mapOfMaps.getOrDefault("aaa",Collections.<String,Integer>emptyMap());

Map<String, Integer> result = mapOfMaps.getOrDefault("aaa",(Map<String,Integer>)Collections.EMPTY_MAP);

甚至

Map<String, Integer> result = mapOfMaps.getOrDefault("aaa",new HashMap<String, Integer>());

有没有办法像那样使用 getOrDefault 还是我必须使用笨拙的方法?

Map<String, Integer> result = mapOfMaps.get("aaa");
if( result == null ) {
  result = Collections.emptyMap();
}

一个可能但仍然相当笨拙的解决方案是辅助函数:

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();
}

然后像这样称呼它

Map<String, Integer> result = getOrEmpty(mapOfMaps,"aaa");

但我仍然更喜欢无需定义额外函数的解决方案。

您可以使用 Collections.unmodifiableMap 查看您的地图 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());

然而,在一行中,它看起来仍然很难看,因为您需要为 unmodifiableMap.

指定泛型类型参数
Map<String, Integer> map = Collections.<String, Map<String, Integer>>
    unmodifiableMap(mapOfMaps).getOrDefault("foo", Collections.emptyMap());

说明

您不能调用任何具有无界或 extends 有界通配符参数的方法,因为通配符的确切类型在编译时未知。

让我们简化一下,看看 Map<String, ? extends Number>,您可以将其分配给

Map<String, ? extends Number> map = new HashMap<String, Integer>();
Map<String, ? extends Number> map = new HashMap<String, Double>();

然而,当调用 map.getOrDefault(Object k, V defaultValue) 时,无法在编译时确定 defaultValue 的类型,因为实际类型可能会在运行时发生变化,即使对于完全相同的赋值(不是虽然是同一个实例)。

// compile-time error, could require a Double or any other Number-type
Number i = map.getOrDefault("foo", (Number)Integer.MAX_VALUE);