嵌套的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
中的每个TypeArgument
,TypeArgument
可能只是一个ReferenceType
而不是 Wildcard
。如果引用类型是 自身 泛型,那么它 可以 具有提供给 it 的类型参数,它们是通配符.
在构造/定义通用类型时,必须指定类型。这两个
? extends A
? extends List<? exetnds A>
正在创建一个新的通配符,它是一个未知的泛型类型(您可以从前面的 ? 看出)。
这两个
B
List<? extends A>
是实际类型。所以当调用
new ArrayList<B>();
new ArrayList<List<? extends A>>();
泛型类型已指定。在一种情况下它是 B
,在另一种情况下它是包含 ? extends A
的 List
。
调用时
新的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
),但它允许在此类表达式中使用嵌套通配符,因为直接通配符类型不是具体类型。
考虑以下 类:
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
中的每个TypeArgument
,TypeArgument
可能只是一个ReferenceType
而不是 Wildcard
。如果引用类型是 自身 泛型,那么它 可以 具有提供给 it 的类型参数,它们是通配符.
在构造/定义通用类型时,必须指定类型。这两个
? extends A
? extends List<? exetnds A>
正在创建一个新的通配符,它是一个未知的泛型类型(您可以从前面的 ? 看出)。
这两个
B
List<? extends A>
是实际类型。所以当调用
new ArrayList<B>();
new ArrayList<List<? extends A>>();
泛型类型已指定。在一种情况下它是 B
,在另一种情况下它是包含 ? extends A
的 List
。
调用时
新的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
),但它允许在此类表达式中使用嵌套通配符,因为直接通配符类型不是具体类型。