为什么此代码有效
ArrayList<?>[] arr = new ArrayList<?>[2];
但下面两个不是?
ArrayList<? extends Object>[] arr = new ArrayList<? extends Object>[2];
ArrayList<? super Object>[] arr = new ArrayList<? super Object>[2];
最后两行生成编译错误;
错误:创建通用数组。
请澄清差异。
更新
另一方面,ArrayList<?>[] arr = new ArrayList<?>[2];
编译良好但
ArrayList<?> arr = new ArrayList<?>();
不是。
这里有几个问题,让我们依次来看:
-
类型绑定(即
extends Object
)只能在声明类型时声明,而不能在实例化对象时使用。例如
ArrayList<? extends Object> ab = new ArrayList<? extends Object>(); // error
ArrayList<? extends Object> ac = new ArrayList<String>(); // okay
-
数组不支持类型参数,例如:
List<Integer>[] arrayOfLists = new List<Integer>[2]; // compile time error
List<Integer> list = new List<Integer>(); // okay
Oracle在此处记录了这种限制的原因。
-
<?>
可以在声明类型参数时使用,也可以与数组一起使用。添加它是为了避免在混合使用泛型和不使用泛型的Java代码时出现"未检查异常"错误。它的意思是"未知的泛型类型"。更多关于无限制通配符的详细信息,请点击此处。ArrayList<?>[] arr = new ArrayList<?>[2];
由于上述原因而有效。然而,它的用途非常有限,因为只有null可以分配给声明为
<?>
的类型。arr[0] = null; // compiles
arr[1] = new Object(); // compile time error
Oracle提供了以下关于使用通配符的指南,这将有助于了解何时使用此通配符。
-
<?>
不能用于实例化对象。例如ArrayList<?> arr = new ArrayList<?>(); // does not compile
ArrayList<?> arr2 = new ArrayList<>(); // but this does
ArrayList<?> arr3 = new ArrayList<String>(); // and so does this
然而,仍然存在使用
<?>
只接受null的问题。arr3.add( " " ); // does not compile even though it was instantiated with String
arr3.add( null ); // compiles just fine
您必须首先了解为什么不允许创建参数化类型的数组。这是因为数组在运行时检查插入的元素是组件类型的实例(la instanceof
)。无法将instanceof
检查为参数化类型,因为对象不具有创建对象时使用的类型参数的意义。instanceof ArrayList<Integer>
在Java中是非法的,与instanceof ArrayList<? extends Number>
类似,但instanceof ArrayList<?>
在Java中被允许,因为它不需要有关对象的类型参数的信息。(顺便说一下,instanceof ArrayList<? extends Object>
和instanceof ArrayList<? super Object>
也是非法的。)
从概念上讲,ArrayList<? extends Object>
几乎与ArrayList<?>
完全相同(有一些细微的差异,但并不相关),但为了语法的一致性,new ArrayList<? extends X>[...]
不允许用于任何X。