之间有什么区别?Java泛型中的对象和对象?


137

我正在使用Eclipse来帮助我清理一些代码以正确使用Java泛型。大多数时候,它在推断类型方面做得非常出色,但是在某些情况下,推断类型必须尽可能地通用:对象。但是Eclipse似乎给了我一个选择,可以在对象类型和“?”类型之间进行选择。

那么之间有什么区别:

HashMap<String, ?> hash1;

HashMap<String, Object> hash2;

4
请参阅有关通配符的官方教程。它很好地说明了这一点,并提供了一个示例,说明为什么仅使用Object才有必要这样做。
本S

Answers:


148

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是很好的参考资料。(您的上述问题也在书中很好地涵盖了。)


1
在所有控制器功能的控制器级别上使用ResponseEntity <?>的正确方法是什么?
Irakli

完美的回答约翰内斯!
gaurav '19

36

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

HashMap<String, ?> hash1;

相当于

HashMap<String, ? extends Object> hash1;

将此知识与Java Generics and Collections的第(2.4)节中的“获取和放置原理”相结合:

获取和放置原则:仅当从结构中获取值时使用扩展通配符,仅将值放入结构中时使用超级通配符,而同时获取和放置时不使用通配符。

希望可以开始使用通配符。


1
如果是“?” 会使您感到困惑,“?extended Object”可能会使您更加困惑。也许。
迈克尔·迈尔斯

试图提供“思维工具”,以使人们能够对这一困难的话题进行推理。提供了有关扩展通配符的其他信息。
朱利安·查斯顿

2
感谢您提供其他信息。我还在消化。:)
skiphoppy

HashMap<String, ? extends Object> 所以它只能防止null被添加到哈希图中?
mallaudin

12

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


1
需要指出的是,这并不完全容易;-),但这是正确的。
肖恩·赖利

6

协方差上方的答案涵盖了大多数情况,但缺少一件事:

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

4

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

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


“您不能安全地将任何内容放入Map <String,?>”。您可以,这就是它的目的。
本S

3
Ben是错误的,您可以放入类型<?>的集合的唯一值是null,而您可以将任何东西放入类型<Object>的集合。
sk。

2
在给出答案的链接中:“由于我们不知道c的元素类型代表什么,因此无法向其中添加对象。” 对于错误信息,我深表歉意。
S

1
最大的问题是,如果我们认为除原语以外的所有东西都是对象,那么我不明白您怎么可能无法在HashMap <String,?>中添加任何内容。还是原始的?
阿瓦隆

1
@avalon在其他地方,有对该地图的引用。例如,它可能是一个Map<String,Integer>。只有Integer对象应该作为值存储在地图中。但是,因为你不知道的值(它的类型?),你不知道它是否安全地调用put(key, "x")put(key, 0)或其他任何东西。
erickson

2

声明hash1HashMap<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()但是,所有对象都有一个方法,因此映射可以为其值使用任何类型的对象。我们仍然可以打印值。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.