我试图更好地理解Java中的泛型,因此编写了一个合并两个集合图的泛型方法。 (在创建硬编码的ArrayList
的那一刻,请忽略它。)
public static <E, K> void mergeMaps(Map<K, Collection<E>> receivingMap, Map<K, Collection<E>> givingMap) {
for (Map.Entry<K, Collection<E>> entry : givingMap.entrySet()) {
Collection<E> someCollection = receivingMap.computeIfAbsent(entry.getKey(), k -> new ArrayList<E>());
someCollection.addAll(entry.getValue());
}
}
我的目标是mergeMaps
函数能够合并其值可以是任意集合(ArrayList
,LinkedHashMap
,...)的地图(相同类型)。< / p>
但是,当我尝试合并Map<Integer, ArrayList<String>>
的两个实例时,遇到了编译时错误,但我不太理解编译器在告诉我什么。
public static void main(String[] args) {
Map<Integer, ArrayList<String>> map1 = new HashMap<>();
Map<Integer, ArrayList<String>> map2 = new HashMap<>();
mergeMaps(map1, map2); // <-- compile error
}
这有什么问题,我该如何解决?
Error:(9, 9) java: method mergeMaps in class CollectionUtil cannot be applied to given types;
required: java.util.Map<K,java.util.Collection<E>>,java.util.Map<K,java.util.Collection<E>>
found: java.util.Map<java.lang.Integer,java.util.ArrayList<java.lang.String>>,java.util.Map<java.lang.Integer,java.util.ArrayList<java.lang.String>>
reason: cannot infer type-variable(s) E,K
(argument mismatch; java.util.Map<java.lang.Integer,java.util.ArrayList<java.lang.String>> cannot be converted to java.util.Map<K,java.util.Collection<E>>)
答案 0 :(得分:3)
方法的签名是
select
Dateadd(s,convert(Bigint,1538397000000/1000,convert(datetime,'1-1-1970'))
然后使用<E, K> void mergeMaps(Map<K, Collection<E>> receivingMap,
Map<K, Collection<E>> givingMap)
作为参数类型的调用是无效的,因为Map<Integer, List<String>>
不是Collection
方法的通用类型参数。
为什么会出现问题?对于泛型,mergeMaps
无法分配给Map<Integer, List<String>>
变量(或以这种方式作为方法参数传递)。这是因为通用类型是不变的(有关更多信息,请参见here。简而言之,这意味着Map<Integer, Collection<String>>
不一定与任何List<Integer>
兼容,尽管List<Number>
与ArrayList<Number>
)。
换句话说,具体参数必须为List<Number>
类型。这是您的第一个解决方案:
Map<Integer, Collection<String>>
如果要允许使用类型为//Solution 1: change your arguments to Map<Integer, Collection<String>>:
Map<Integer, Collection<String>> map1 = new HashMap<>();
Map<Integer, Collection<String>> map2 = new HashMap<>();
mergeMaps(map1, map2);
的参数进行调用,则必须更改目标方法以在映射值周围引入通用参数:
Map<Integer, List<String>>
这可以在将值声明为public static <E, K, C extends Collection<E>> void
mergeMaps2(Map<K, C> receivingMap, Map<K, C> givingMap) {
for (Map.Entry<K, C> entry : givingMap.entrySet()) {
Collection<E> someCollection = receivingMap.computeIfAbsent(entry.getKey(),
k -> (C) new ArrayList<E>());
someCollection.addAll(entry.getValue());
}
}
的子类型的映射中调用(只要两个参数中的Collection<E>
类型相同)即可。
Collection
旁注(或题外话)
现在,当您对此进行编译时,您还有另一个问题:此行上有一个编译器警告:
Map<Integer, List<String>> map1 = new HashMap<>();
Map<Integer, List<String>> map2 = new HashMap<>();
mergeMaps2(map1, map2);
Map<Integer, Set<String>> map1 = new HashMap<>();
Map<Integer, Set<String>> map2 = new HashMap<>();
mergeMaps2(map1, map2);
声称Collection<E> someCollection =
receivingMap.computeIfAbsent(entry.getKey(), k -> (C) new ArrayList<E>());
是未经检查的演员表。为什么这个?让我们看一下上面的示例调用(我建议将两个示例相加):
电话1:
(C) new ArrayList<E>()
在此示例中,Map<Integer, List<String>> map1 = new HashMap<>();
Map<Integer, List<String>> map2 = new HashMap<>();
mergeMaps2(map1, map2);
意味着将receivingMap.computeIfAbsent(entry.getKey(), k -> (C) new ArrayList<E>())
的实例作为值添加到映射。由于实际对象 的类型与调用者的声明类型(ArrayList<String>
)兼容,因此可以。
现在,您认为这会做什么?
电话2:
List<String>
在这种情况下,不幸的是,Map<Integer, Set<String>> map1 = new HashMap<>();
Map<Integer, Set<String>> map2 = new HashMap<>();
mergeMaps2(map1, map2);
仍将尝试添加一个receivingMap.computeIfAbsent(entry.getKey(), k -> (C) new ArrayList<E>())
,它恰巧与呼叫者的期望值类型({{1} }。
在具体类型参数的上下文中,编译器不能确定强制转换ArrayList<String>
始终是正确的。它放弃了,但发出警告以警告开发人员。
处理这个问题实际上是一个棘手的问题。您需要知道要实例化的类型,但是您的方法参数将不允许您这样做,因为您不能仅运行Set<String>
。您自己的要求和设计将确定正确的解决方案,但我将以一种可能的解决方案结束:
(C) new ArrayList<E>()
答案 1 :(得分:0)
错误显示java.util.List<java.lang.String>> cannot be converted to java.util.Map<K,java.util.Collection<E>>)
您必须修改您的方法:
public static <E, K> void mergeMaps(Map<K, List<E>> receivingMap, Map<K, List<E>> givingMap) {
for (Map.Entry<K, List<E>> entry : givingMap.entrySet()) {
Collection<E> someCollection = receivingMap.computeIfAbsent(entry.getKey(), k -> new ArrayList<E>());
someCollection.addAll(entry.getValue());
}
}