我正在尝试开发一个通用函数来过滤地图。
我到目前为止的代码是:
public static Map<?, ?> filterAttrs(Map<?, ?> args, String... unless) {
Map<?, ?> filteredAttrs = Map.class.newInstance();
Arrays.sort(unless);
for (Object o : args.keySet()) {
if (Arrays.binarySearch(unless, o.toString()) < 0 ) {
filteredAttrs.put(o, args.get(o));
}
}
return filteredAttrs;
}
我在filteredAttrs.put
中收到以下错误在Map类型中放置(捕获#5-of?,捕获#6-of?)的方法不适用于参数(对象,捕获#8-of?)
我不知道如何实例化泛型Map
(我试过1Map.class.newInstance()`)。
有什么想法吗?
编辑:在阅读了许多答案后,问题似乎是如何使filteredAttrs
成为与args
相同类型的实例。 (Map) args.getClass().newInstance()
似乎可以解决问题。
答案 0 :(得分:8)
此代码的问题在于类型系统阻止您将对象放入其键类型为Map
的{{1}}中。这是因为如果密钥类型为?
,则编译器不知道实际存储在地图中的内容 - 可以是?
,Object
或Integer
- 因此无法确认您尝试添加到地图中的内容实际上是正确的类型,并且不会在List<Object>
中不合适。例如,如果你有这个方法:
Map
然后编写如下代码:
public static void breakMyMap(Map<?, ?> m) {
m.put(new Object(), new Object()); // Won't compile
}
然后,如果Map<String, String> myMap = new HashMap<String, String>();
breakMyMap(myMap);
中的代码要编译,它会将一对breakMyMap
作为键和值放入Object
,打破所有元素确实存在的不变量Map<String, String>
秒。
要解决此问题,请更改函数,以便您拥有关于键和值的更多类型信息,而不是让此函数在String
上运行。例如,你可以试试这个:
Map<?, ?>
既然编译器知道密钥类型为public static <K, V> Map<K, V> filterAttrs(Map<K, V> args, String... unless) {
Map<K, V> filteredAttrs = new HashMap<K, V>();
Arrays.sort(unless);
for (K o : args.keySet()) {
String attr = o.toString();
if (Arrays.binarySearch(unless, o.toString()) < 0 ) {
filteredAttrs.put(o, args.get(o));
}
}
return filteredAttrs;
}
,它就可以验证K
是否会混淆地图中密钥的类型。
我应该指出的另一件事是你所拥有的代码即使编译也不会有效。原因是该行
put
在运行时会导致异常,因为Map<?, ?> filteredAttrs = Map.class.newInstance();
是接口,而不是类,因此尝试使用Map
创建它的实例将无法正常工作正确。要解决这个问题,您可以显式指定地图的类型(正如我在上面的代码中所做的那样),或者获取参数的类:
newInstance
当然,这也不能保证也能正常工作,尽管集合的一般合同是所有集合应该都有一个无参数的构造函数。
希望这有帮助!
答案 1 :(得分:5)
如果人们重新发明轮子(并没有做得更好),我认为这是不好的。
要在一个建议中修复所有问题:您可以尝试Google Guava吗?
例如,您可以使用Maps:
或者至少编写一个以谓词为参数的过滤器。
答案 2 :(得分:3)
这一行
Map<?, ?> filteredAttrs = Map.class.newInstance();
应抛出InstantiationException - 因为Map
是一个接口 - 除非你已经实现了自己的名为Map
的类
答案 3 :(得分:3)
你不能把任何东西放到Map<?, ?>
中,因为编译器并不确切知道它是什么类型的地图。在该行:
filteredAttrs.put(o, args.get(o));
您假装Map<?, ?>
是Map<Object, Object>
,但事实并非如此。 Angelika Langer的Java Generics FAQ更详细地解释了why this doesn't work。
您应该为方法添加类型参数;那么也没有必要通过反射创建新地图:
public static <K, V> Map<K, V> filterAttrs(Map<K, V> args, String... unless) {
Map<K, V> filteredAttrs = new HashMap<K, V>();
Arrays.sort(unless);
for (K o : args.keySet()) {
if (Arrays.binarySearch(unless, o.toString()) < 0) {
filteredAttrs.put(o, args.get(o));
}
}
return filteredAttrs;
}
答案 4 :(得分:2)
您无法实例化Map,因为它是一个接口。不要使用?
因为它会引起混淆。
public static <T, K> Map<T, K> filterAttrs(Map<T, K> args, T... unless) {
Map<T, K> filteredAttrs;
filteredAttrs = new HashMap<T, K>();
Arrays.sort(unless);
for (T o : args.keySet()) {
if (Arrays.binarySearch(unless, o) < 0) {
filteredAttrs.put(o, args.get(o));
}
}
return filteredAttrs;
}
你有这条线什么都不做,所以我把它删除了: -
String attr = o.toString();