嵌套的ArrayList初始化有什么问题吗?

时间:2018-03-20 17:22:00

标签: java generics collections bounded-wildcard

考虑以下几个类:

class A{ }
class B extends A{ }

我们知道编译好了:

List<? extends A> xx = new ArrayList<B>();
List<? extends List<? extends A>> xy = new ArrayList<List<? extends A>>();

但这会产生编译时错误

List<? extends A> yx = new ArrayList<? extends A>();
List<? extends List<? extends A>> yy = new ArrayList<? extends List<? extends A>>();

错误说:

  

必需:没有边界的类或接口

我知道编译器为上述初始化解释的新值是不同的,因此无法安全地转换它们。 但是,没有界限的是什么?在上面的错误消息中意味着什么?

3 个答案:

答案 0 :(得分:4)

此错误指的是创建新的ArrayList,其直接顶级类型参数使用通配符。尽管允许嵌套类型参数具有通配符,但这是不允许的。

JLS, Section 15.9, "Class Instance Creation Expressions",声明:

  

如果 TypeArguments 紧接在new之后,或紧接在(之前,那么如果任何类型参数是通配符({{},则它是编译时错误3}})。

这里的关键词是&#34;立即&#34;,因为它代表直接类型参数,而不是嵌套类型参数。

以下是§4.5.1中提到的限制:

  

它们[通配符]不能用于创建对象或数组,也就是说,新表达式中不允许使用通配符实例化。通配符实例化不是类型,它们是来自类型族的成员的占位符。在某种程度上,通配符实例化类似于接口:我们可以声明接口类型的变量,但是我们不能创建接口类型的对象;创建的对象必须是实现接口的类类型。与通配符实例化类似:我们可以声明通配符实例化类型的变量,但是我们不能创建这种类型的对象;创建的对象必须是通配符实例化指定的实例化系列的具体实例。

(强调我的)

基本上,通配符类型不是具体类型,无法实例化,类似的原因是不允许直接创建接口实例。

但是,这并未说明嵌套通配符,这些通配符与最初不相关的对象的创建有关,这些对象最终可能与此类型相关联。在您的示例中,这将是可以添加到外部List的嵌套ArrayList。它们可能是对任何匹配通配符类型的List的引用,但它们并未在此处创建。

<强>摘要

Java不允许在实例创建表达式中使用通配符(使用new),但它允许在这样的表达式中使用嵌套通配符,因为直接通配符类型不是具体类型。 / p>

答案 1 :(得分:2)

正在创建的类实例的类型参数不能是通配符(§15.9):

  

如果在[{1}}之后或紧接在new之前存在[类型参数],那么如果任何类型参数是通配符,则它是编译时错误。

这就是它。类型参数可能包含通配符(如(中所示),但不能直接给出通配符(如new ArrayList<List<?>>中所示)。

上述引用引用的类型参数的语法(§4.5.1)可能会澄清这种区别:

new ArrayList<?>

换句话说,对于提供给类实例创建表达式的TypeArguments: < TypeArgumentList > TypeArgumentList: TypeArgument {, TypeArgument} TypeArgument: ReferenceType Wildcard Wildcard: {Annotation} ? [WildcardBounds] WildcardBounds: extends ReferenceType super ReferenceType 中的每个TypeArgumentTypeArgumentList可能只是TypeArgument而不是{{1} }}。如果引用类型本身泛型,那么可以具有提供给 it 的类型参数,这些参数是通配符。

答案 2 :(得分:0)

在构造/防御泛型类型时,必须指定类型。这两个

? extends A
? extends List<? exetnds A>

正在创建一个新的通配符,这是一种未知的泛型类型(你可以从前面的?中看出来。)

这两个

B
List<? extends A>

是实际类型。所以在打电话时

new ArrayList<B>();
new ArrayList<List<? extends A>>();

指定了泛型类型。在一次案例中,它是B,在另一种情况下,它是包含List的{​​{1}}。

致电

new ArrayList(); 新的ArrayList&gt;();

两次都没有定义泛型类型。这是不允许的。

? extends A不会导致错误的原因是,因为这是一种类型。原始List<? extends A>中的列表尚未创建。您只能在以后定义它必须是List<? extends List<? extends A>>。如果您尝试像这样创建此列表

List<? extends A>

你会遇到同样的问题。

可理解?