7

好的,所以 Java 不允许以下内容:

Foo<?> hello = new Foo<?>();

这是有道理的——毕竟,如果你无论如何都要装箱/拆箱,那么泛型有什么意义呢?

奇怪的是,Java确实允许这样做:

Foo<Bar<?>> howdy = new Foo<Bar<?>>();

诚然,这实际上完成了更多,尽管在某些时候,会有一个演员来获得任何Bar正在使用的东西。但是,如果 Java 在某些特定性上是可以接受的,为什么它不允许这样做呢?:

Foo<? extends Mal> bonjour = new Foo<? extends Mal>();

我问的唯一原因是我正在修复依赖“构造函数的类参数中的通配符”,并且非常想知道它背后的含义/意图。

编辑:为了澄清我的问题,允许/不允许这些陈述的依据是什么?我知道“Java 不允许在构造函数中使用通配符”,但问题是,为什么所有这些怪异?如果嵌套通配符可以,为什么不允许有界通配符?

4

4 回答 4

5

至于基本原理:new Foo<?>应该最好写成new Foo<Object>,所以这里的编译器限制强制编写尽可能可读的代码。最后一个例子也可以是new Foo<Mak>(),因为在 a 上没有什么是你不能在 aFoo<? extends Mal>上做的Foo<Mal>。请注意,反之亦然:aFoo<Mal>可能会接受a不接受Mal的参数。Foo<? extends Mal>

另一方面,您可能确实需要一个Foo可以处理Bar任何类型对象的对象,因此Foo<Bar<?>>非常有意义。如果您只访问Bar不依赖类型参数的方法,就会出现这种情况。编译器在这里没有什么可抱怨的。

于 2012-07-18T22:20:33.903 回答
3

您的第一个和第三个声明不起作用的原因是因为JLS §15.9

如果类实例创建表达式中使用的任何类型参数是通配符类型参数(第 4.5.1 节) ,则会出现编译时错误。

在您的第一个声明中,?是通配符类型。在您的第三个声明中,? extends Mal是通配符类型。

您的第二个声明确实有效的原因Bar<?>是不是通配符类型。Bar<?>的类型是Bar.

“构造函数的类参数中的通配符”是什么意思?

于 2012-07-18T22:17:13.637 回答
1

当您实例化参数化类型的实例时,您必须指定一个类型。?并且? extends Mal不是类型。Bar<?>是一种类型。您可以在此页面上找到更多信息。

这是您的第二种情况的示例:

Bar<Object> bar1 = ...;
Bar<String> bar2 = ...;
List<Bar<?>> list = new ArrayList<Bar<?>>();
list.add(bar1);
list.add(bar2);

您可以想象将 Bar 实例存储在列表中,但您不需要知道它们的类型参数。

于 2012-07-18T22:16:57.013 回答
1

new Foo<?>可以等效地替换为满足该类型参数边界的任何类型new Foo<SomeRandomTypeIMadeUp>SomeRandomTypeIMadeUp最简单的选择是简单地选择该类型参数的上限,例如,如果它是class Foo<T extends X>,那么new Foo<X>就足够了。

你可能会问,为什么我可以选择任意类型的参数,即使是与我的程序的其余部分完全没有联系的类型参数?那不是不安全吗?答案是否定的,因为这正是它的Foo<?>意思——类型参数可以是任何东西,你不能依赖它是什么。这表明您要求做的事情是绝对荒谬的。用它创建的东西new Foo<?>几乎完全没用,因为你不能用它做任何事情,这取决于类型参数的类型。

带有通配符的类型通常很有用。例如,你可以有一个类型的参数,List<?>你可以将任何类型的参数传递List给它,它只是从列表中取出东西。但在这种情况下,您并没有创建列表。创建列表并将其传递给您的函数可能有一些非通配符类型参数。在该功能的范围内,您仍然可以将东西放入列表并用它做有用的事情。如果一个函数要创建一个List<?>; 这将毫无用处——您不能将任何元素null放入其中。

这就是为什么你不能这样做new Foo<?>:它完全没用;如果你想使用泛型,你可能使用错了。在极少数情况下你真的想要它,有一个现成的替代品,new Foo<AnyTypeThatSatisfiesTheBounds>.

Foo<Bar<?>>非常不同。Bar<?>特定类型。Foo<Bar<?>>并不意味着您可以分配Foo<Bar<Something>>给它;相反,这是非法的;Foo如果它们不是通配符,则其类型参数必须匹配。与通配符不同的是,使用 aList<Bar<?>>时,您可以将对象放入其中并从中取出对象。

于 2012-07-18T23:06:12.650 回答