通用列表数组

时间:2011-10-18 15:50:43

标签: java generics

我正在使用Generic和数组,看起来下面的代码编译得很好,

ArrayList<Key> a = new ArrayList<Key>();

但是编译器抱怨这个,

ArrayList<Key>[] a = new ArrayList<Key>[10];

通过阅读stackoverflow中的帖子,我有点明白这是由于类型擦除而我可以通过使用来修复它,

ArrayList<Key>[] a = (ArrayList<Key> []) new ArrayList[10];

或列表清单

ArrayList<ArrayList<Key>> b = new ArrayList<ArrayList<Key>>();

但我无法弄清楚幕后背后的原因。特别是,为什么第二个是非法的,因为第一个是非常好的。为什么编译器不会抱怨列表列表。

5 个答案:

答案 0 :(得分:22)

您不能拥有数组,因为数组需要原始类型。你在第二个实例中对它进行了类型转换,这使得它适合定义的类型,因此是合法的(但是,它不可能推断)。列表列表是合法的,因为ArrayList不是数组。

阅读official tutorial中的第7.3章(第15页),了解更多详情。

  

数组对象的组件类型可能不是类型变量或   参数化类型,除非它是(无界)通配符类型。你可以   声明其元素类型为类型变量或a的数组类型   参数化类型,但不是数组对象。   这很烦人,可以肯定。这种限制对于避免以下情况是必要的:

List<String>[] lsa = new List<String>[10]; // not really allowed
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
oa[1] = li; // unsound, but passes run time store check
String s = lsa[1].get(0); // run-time error - ClassCastException
     

如果允许参数化类型的数组,上面的例子会   编译时没有任何未经检查的警告,但在运行时失败。

然后教程接着说出以下内容:

  

由于类型变量在运行时不存在,因此无法确定是什么   实际的数组类型。   解决这些限制的方法是使用类文字作为运行时   类型代币

答案 1 :(得分:5)

数组是穷人的仿制药;对于真正的泛型,人们应该避免使用数组,尽管并非总是可行。

阵列是协变的,泛型是不变的;结合擦除,事情本身并不适合,正如克里斯答案中的例子所示。

但是我认为可以放宽规范以允许创建通用数组 - 那里确实没有问题。向上铸造阵列时会出现危险;此时的编译器警告就足够了。

实际上Java确实为vararg方法创建了通用数组,所以它有点虚伪。

以下是利用这一事实的实用方法

@SafeVarargs
static <E> E[] arrayLiteral(E... array)
{
    return array;
}

@SafeVarargs
static <E> E[] newArray(int length, E... array)
{
    return Arrays.copyOf(array, length);
}

// usage

    List<String>[] array1 = arrayLiteral(list, list);

    List<String>[] array2 = newArray(10);

答案 2 :(得分:4)

我自己有similar question - FWIW,我没有找到有说服力的答案。最详细答案(参考pdf参考)的相关部分是:

  

数组对象的组件类型可能不是类型变量或   参数化类型,除非它是(无界)通配符类型。你可以   声明其元素类型为类型变量或a的数组类型   参数化类型,但不是数组对象。这很烦人   当然。这种限制是必要的,以避免像

这样的情况
       List<String>[] lsa = new List<String>[10]; // not really allowed
       Object o = lsa;
       Object[] oa = (Object[]) o;
       List<Integer> li = new ArrayList<Integer>();
       li.add(new Integer(3));
       oa[1] = li; // unsound, but passes run time store check
       String s = lsa[1].get(0); // run-time error - ClassCastException

因为我可以将List []捕获到Object [],然后将不正确的东西推入Object [],然后从List引用中错误地引用,通过转换引用,这是坏/不允许的?但只有新的?

对我而言,如果用新的方式宣称这个问题或多或少是一个问题而不是使用它仍然有点模糊,仍然盯着它看着它,希望它会开始有意义,或至少解析为一个漂亮的3D图像。

答案 3 :(得分:2)

创建通用数组不是类型安全的(请参阅Joshua Bloch撰写的“有效Java - 第二版”中的“第25项:首选列表到数组”)。

使用:

 List<List<Key>> b = new ArrayList<List<Key>>(10);

或者使用Java SE 7:

 List<List<Key>> b = new ArrayList<>(10);

答案 4 :(得分:1)

数组允许转义类型检查(如Chris的回答所示)。因此,您可以拥有一个通过所有编译器检查的代码(编译器没有“未检查”警告),但在运行时使用ClassCastException会失败。 禁止这种结构会给开发人员带来问题,因此会出现警告。