创建无界和有界外卡类型数组之间的区别?

时间:2014-10-01 10:54:04

标签: java arrays generics wildcard bounded-wildcard

为什么此代码有效

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

2 个答案:

答案 0 :(得分:13)

这里有一些问题,让我们依次看一下:

  1. 类型绑定(即extends Object)只能在声明类型时声明,在实例化对象时不能使用。

    例如

    ArrayList<? extends Object> ab = new ArrayList<? extends Object>(); // error ArrayList<? extends Object> ac = new ArrayList<String>(); // okay

  2. 数组不支持类型参数,例如:

    List<Integer>[] arrayOfLists = new List<Integer>[2]; // compile time error List<Integer> list = new List<Integer>(); // okay

    Oracle记录了此限制的原因here

  3. 声明类型参数和数组时,可以使用
  4. <?>。添加它是为了在混合使用和不使用泛型的Java代码时帮助避免“未经检查的异常”错误。它意味着'未知的通用类型'。关于无界外卡here的更多细节。

    ArrayList<?>[] arr = new ArrayList<?>[2];

    由于上述原因,

    有效。但是它的使用非常有限,因为只能将null赋给声明为<?>的类型。

    arr[0] = null; // compiles

    arr[1] = new Object(); // compile time error

    Oracle提供以下Guide on using wildcards,这将有助于了解何时使用此通配符。

  5. <?>不能用于实例化对象。例如

    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

答案 1 :(得分:3)

您必须首先了解为什么不允许创建参数化类型的数组。这是因为数组在运行时检查插入的元素是组件类型的实例(la instanceof)。无法检查instanceof参数化类型,因为对象不具有创建它的类型参数的含义。 instanceof ArrayList<Integer>在Java中是非法的,类似于instanceof ArrayList<? extends Number>,但在Java中允许instanceof ArrayList<?>,因为它不需要有关对象的类型参数的信息。 (顺便说一下instanceof ArrayList<? extends Object>instanceof ArrayList<? super Object>也是非法的。)

从概念上讲,ArrayList<? extends Object>几乎与ArrayList<?>完全相同(存在细微差别但不相关),但为了语法的一致性,任何X都不允许new ArrayList<? extends X>[...]。 / p>