Answers:
HashMap<String, String>
匹配项的实例,Map<String, ?>
但不是Map<String, Object>
。假设您要编写一个接受String
s到任何内容的映射的方法:如果您要编写
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
的的的InputStream
s和的子类型的方法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()
但是,所有对象都有一个方法,因此映射可以为其值使用任何类型的对象。我们仍然可以打印值。