Answers:
HashMap<String, String>匹配项的实例,Map<String, ?>但不是Map<String, Object>。假设您要编写一个接受Strings到任何内容的映射的方法:如果您要编写
public void foobar(Map<String, Object> ms) {
...
}
您无法提供HashMap<String, String>。如果你写
public void foobar(Map<String, ?> ms) {
...
}
有用!
在Java泛型中有时会被误解的一件事是,List<String>它不是的子类型List<Object>。(但String[]实际上是的子类型Object[],这是泛型和数组不能很好混合的原因之一。(Java中的数组是协变的,泛型不是,它们是不变的))。
示例:如果您想编写一个接受List的的的InputStreams和的子类型的方法InputStream,则可以编写
public void foobar(List<? extends InputStream> ms) {
...
}
顺便说一句:当您想了解Java中不那么简单的内容时,Joshua Bloch的Effective Java是很好的参考资料。(您的上述问题也在书中很好地涵盖了。)
考虑这个问题的另一种方法是
HashMap<String, ?> hash1;
相当于
HashMap<String, ? extends Object> hash1;
将此知识与Java Generics and Collections的第(2.4)节中的“获取和放置原理”相结合:
获取和放置原则:仅当从结构中获取值时使用扩展通配符,仅将值放入结构中时使用超级通配符,而同时获取和放置时不使用通配符。
希望可以开始使用通配符。
HashMap<String, ? extends Object> 所以它只能防止null被添加到哈希图中?
协方差上方的答案涵盖了大多数情况,但缺少一件事:
“?” 在类层次结构中包含“对象”。您可以说String是Object的一种,而Object是?的一种。不是所有的对象都匹配,但是所有的对象都匹配?。
int test1(List<?> l) {
return l.size();
}
int test2(List<Object> l) {
return l.size();
}
List<?> l1 = Lists.newArrayList();
List<Object> l2 = Lists.newArrayList();
test1(l1); // compiles because any list will work
test1(l2); // compiles because any list will work
test2(l1); // fails because a ? might not be an Object
test2(l2); // compiled because Object matches Object
您无法安全地将任何内容放入Map<String, ?>,因为您不知道值应该是什么类型。
您可以将任何对象放入Map<String, Object>,因为该值已知为Object。
Map<String,Integer>。只有Integer对象应该作为值存储在地图中。但是,因为你不知道的值(它的类型?),你不知道它是否安全地调用put(key, "x"),put(key, 0)或其他任何东西。
声明hash1为HashMap<String, ?>,表明该变量hash1可以容纳HashMap具有键String和值的任何类型。
HashMap<String, ?> map;
map = new HashMap<String, Integer>();
map = new HashMap<String, Object>();
map = new HashMap<String, String>();
上面所有这些都是有效的,因为该变量 map可以存储任何这些哈希映射。该变量并不关心其持有的哈希表的Value类型是什么。
有一个通配符也没有,但是,让你把任何类型的对象到您的地图。实际上,使用上面的哈希图,您无法使用map变量在其中添加任何内容:
map.put("A", new Integer(0));
map.put("B", new Object());
map.put("C", "Some String");
上述所有方法调用都将导致编译时错误,因为Java不知道内部HashMap的Value类型map是什么。
您仍然可以从哈希映射中获取一个值。尽管您“不知道值的类型”(因为您不知道变量中包含哪种类型的哈希图),但是您可以说所有内容都是该图的子类Object,因此,无论您从该图中得到什么将为对象类型:
HashMap<String, Integer> myMap = new HashMap<>();// This variable is used to put things into the map.
myMap.put("ABC", 10);
HashMap<String, ?> map = myMap;
Object output = map.get("ABC");// Valid code; Object is the superclass of everything, (including whatever is stored our hash map).
System.out.println(output);
上面的代码块将在控制台输出10。
因此,要结束,HashMap请在不关心(即无所谓)什么类型的情况下使用带通配符的a HashMap,例如:
public static void printHashMapSize(Map<?, ?> anyMap) {
// This code doesn't care what type of HashMap is inside anyMap.
System.out.println(anyMap.size());
}
否则,请指定所需的类型:
public void printAThroughZ(Map<Character, ?> anyCharacterMap) {
for (int i = 'A'; i <= 'Z'; i++)
System.out.println(anyCharacterMap.get((char) i));
}
在上述方法中,我们需要知道Map的键是一个Character,否则,我们将不知道使用哪种类型从中获取值。toString()但是,所有对象都有一个方法,因此映射可以为其值使用任何类型的对象。我们仍然可以打印值。