Java中未绑定的通配符generics的用途和要点是什么?

我不明白未绑定的通配符generics的用途是什么。 带有上边界的绑定通配符generics 非常有意义,因为使用多态,我可以使用该类型或集合。 但是具有任何类型的generics有什么意义呢? 它不会破坏仿制药的目的吗? 编译器没有发现任何冲突,在类型擦除后,就像没有使用generics一样。

当您的方法并不真正关心实际类型时,未绑定类型可能很有用。

一个原始的例子是这样的:

 public void printStuff(Iterable stuff) { for (Object item : stuff) { System.out.println(item); } } 

由于PrintStream.println()可以处理所有引用类型(通过调用toString() ),因此我们不关心 Iterable的实际内容是什么。

调用者可以传入ListSetCollection> Collection>

还要注意,不使用generics(使用原始类型调用)会产生完全不同的效果:它使编译器处理整个对象 ,就好像generics根本不存在一样 。 换句话说:不仅忽略类的类型参数,还忽略方法上的所有generics类型参数。

另一个重要的区别是您不能向Collection添加任何(非null )值,但可以将所有对象添加到原始类型Collection

这将无法编译,因为c的类型参数是未知类型(=通配符? ),因此我们无法提供保证可分配给它的值( null除外,它可分配给所有引用类型)。

 Collection c = new ArrayList(); c.add("foo"); // compilation error 

如果将类型参数保留(即使用原始类型),则可以向集合中添加任何内容

 Collection c = new ArrayList(); c.add("foo"); c.add(new Integer(300)); c.add(new Object()); 

请注意,编译器将警告您不要使用原始类型,特别是出于这个原因:它删除与generics相关的任何类型检查。

当您需要执行检查instanceof

你不能像这样参数化:

 Object value; if (value instanceof List) { // ... } 

所以你也是:

 Object value; if (value instanceof List) { // ... } 

虽然使用原始类型意味着您不了解generics(因为您很懒惰或很久以前编写过代码),使用意味着您了解generics并明确强调您的代码可以使用任何类型的对象。

对于未绑定的通配符,有(罕见的)完全正确的用例。 SDK包含其中一些。

一个示例是一种方法,它对任何类型的列表执行明确的操作,并且不会在Collections返回任何rotate

 static void rotate(List list, int distance) 

另一个例子是当你想列出类的可能构造函数时,方法是:

 Constructor[] getConstructors() 

在这里它甚至不可能使用generics,因为根据定义,数组将包含不同的构造函数,每个构造函数都有自己的实际类。 相比之下,API确实使用通用签名来获取一个构造函数: Constructor getConstructor(Class... parameterTypes)

结论是,即使它主要用于与旧代码的兼容性,仍然存在未绑定的通配符generics是正确方法的地方。

在包装不使用generics的旧代码时,使用无界通配符只是有意义的,AFAIK,基本上是集合。

如果你看看你能用这种通用做什么,基本上没什么 。 如果你有一个集合你不能添加任何东西,如果你尝试阅读一些东西,你将总是得到一个Object ,等等。

这反过来有助于保证您将以类型安全的方式处理数据,而使用原始类型会导致编译器忽略您所做的任何混乱。

通过通配符参数化类型的引用变量可访问/不可访问哪些方法和字段? 来自Angelika Langers Java Generics FAQ可能会引起关注。

请允许我重新解释一下这个问题:

ListList之间有什么区别?”

答案是List更具限制性。 它告诉我们,我们有一堆某种类型的对象,但该类型不一定是Object

由于我们不知道该类型是什么,我们无法添加到列表中 – 我们添加的任何内容都可能是错误的类型。 事实上,我们不能传递任何论据? 键入任何方法,而不仅仅是add()

从好的方面来说,当我们指定一个方法采用List ,它可以采用ListList或任何其他List<>List只能采用List