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

Is there something wrong with nested ArrayList Initialization?

考虑以下 类:

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>>();

错误说:

required: class or interface without bounds

我知道编译器为上述初始化解释的新值是不同的,因此不能安全地转换它们。 但是上面的错误信息中的'without bounds'是什么意思呢?

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

If [type arguments are] present immediately after new, or immediately before (, then it is a compile-time error if any of the type arguments are wildcards.

差不多就是这些了。类型参数可以包含通配符(如new ArrayList<List<?>>),但不能直接给出通配符(如new ArrayList<?>)。

以上引用 (§4.5.1):

的类型参数语法可能会阐明这种区别
TypeArguments:
    < TypeArgumentList >

TypeArgumentList:
    TypeArgument {, TypeArgument}

TypeArgument:
    ReferenceType
    Wildcard

Wildcard:
    {Annotation} ? [WildcardBounds]

WildcardBounds:
    extends ReferenceType
    super ReferenceType

换句话说,对于提供给class实例创建表达式的TypeArgumentList中的每个TypeArgumentTypeArgument可能只是一个ReferenceType而不是 Wildcard。如果引用类型是 自身 泛型,那么它 可以 具有提供给 it 的类型参数,它们是通配符.

在构造/定义通用类型时,必须指定类型。这两个

? extends A
? extends List<? exetnds A>

正在创建一个新的通配符,它​​是一个未知的泛型类型(您可以从前面的 ? 看出)。

这两个

B
List<? extends A>

是实际类型。所以当调用

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

泛型类型已指定。在一种情况下它是 B,在另一种情况下它是包含 ? extends AList

调用时

新的ArrayList(); 新 ArrayList>();

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

之所以List<? extends A>没有报错,是因为这里是一个类型。原始 List<? extends List<? extends A>> 中的列表尚未创建。你只是定义它必须是一个List<? extends A>。如果您尝试像这样创建此列表

new ArrayList<? extends A>();

你会运行遇到同样的问题。

可以理解吗?

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

JLS, Section 15.9, "Class Instance Creation Expressions",指出:

If TypeArguments is present immediately after new, or immediately before (, then it is a compile-time error if any of the type arguments are wildcards (§4.5.1).

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

这里是Angelika Langer's article about generics and its usages中提到的限制:

They [wildcards] can not be used for creation of objects or arrays, that is, a wildcard instantiation is not permitted in a new expression. Wildcard instantiations are not types, they are placeholders for a member from a family of types. In a way, a wildcard instantiation is similar to an interface: we can declare variables of interface types, but we cannot create objects of interface types; the created objects must be of a class type that implements the interface. Similar with wildcard instantiations: we can declare variables of a wildcard instantiated type, but we cannot create objects of such a type; the created objects must be of a concrete instantiation from the family of instantiations designated by the wildcard instantiation.

(强调我的)

基本上,通配符类型不是具体类型,不能实例化,与不允许直接创建接口实例类似。

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

总结

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