Java 泛型中? 和 Object 的区别是什么?

我使用 Eclipse 来帮助我清理一些代码,以便正确地使用 Java 泛型。大多数情况下,它在推断类型方面做得非常好,但是在某些情况下,推断的类型必须尽可能通用: Object。但是 Eclipse 似乎给了我一个选项,让我在 Object 类型和‘ ?’类型之间做出选择.

那么,这两者之间的区别是什么:

HashMap<String, ?> hash1;

还有

HashMap<String, Object> hash2;
40139 次浏览

HashMap<String, String>的一个实例匹配 Map<String, ?>,但不匹配 Map<String, Object>。假设您想编写一个接受从 String到任何东西的映射的方法: If you would write

public void foobar(Map<String, Object> ms) {
...
}

你不能提供 HashMap<String, String>。如果你写

public void foobar(Map<String, ?> ms) {
...
}

成功了!

在 Java 的泛型中有时会被误解的一点是,List<String>不是 List<Object>的子类型。(但是 String[]实际上是 Object[]的一个子类型,这是泛型和数组不能很好地混合的原因之一。(Java 中的数组是协变的,泛型不是,它们是 不变)。

样本: 如果您想编写一个接受 InputStreamListInputStream的子类型的方法,那么您应该编写

public void foobar(List<? extends InputStream> ms) {
...
}

顺便说一下: 当你想了解 Java 中不那么简单的东西时,约书亚 · 布洛赫的有效 Java是一个很好的资源。(你上面的问题在本书中也有很好的阐述。)

您不能安全地将任何内容放入 Map<String, ?>,因为您不知道这些值应该是什么类型。

您可以将任何对象放入 Map<String, Object>中,因为已知该值为 Object

如果您记得 Collection<Object>只是一个包含 Object类型对象的泛型集合,那么很容易理解,但是 Collection<?>是所有类型集合的超级类型。

另一种思考这个问题的方法是

HashMap<String, ?> hash1;

相当于

HashMap<String, ? extends Object> hash1;

将这些知识与 Java 泛型和集合第(2.4)节中的“获取和放置原则”结合起来:

获取和放置原则: 使用 扩展通配符时,仅获取 值,使用 super 当您只将值放入 结构,并且不要使用通配符 当你们两个。

希望这张未知牌能够开始变得更有意义。

上述协方差的答案涵盖了大多数情况,但遗漏了一点:

"?"在类层次结构中包含“对象”。你可以说 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

hash1声明为 HashMap<String, ?>表示变量 hash1可以保存任何键值为 StringHashMap和任何类型的值。

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 不知道 map中 HashMap 的 Value 类型是什么。

您仍然可以从散列映射中获取值。虽然你“不知道值的类型”(因为你不知道变量中散列映射的类型) ,但是你可以说所有的东西都是 Object的子类,因此,不管你从映射中得到什么,都是 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的类型时,使用带通配符的 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()方法,因此映射的值可以有任何类型的对象。我们仍然可以打印这些值。