泛型列表数组

2022-09-01 08:59:46

我正在玩泛型和数组,似乎下面的代码编译得很好,

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

但我无法弄清楚幕后的原因。特别是,为什么第二个是非法的,因为第一个是完全可以的。以及为什么编译器不抱怨列表的列表。


答案 1

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

有关此内容的更多详细信息,请阅读官方教程中的第 7.3 章(第 15 页)。

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

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

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

然后,本教程继续说以下内容:

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


答案 2

阵列是穷人的泛型;对于真正的泛型,人们应该避免数组,尽管并不总是可能的。

数组是协变的,泛型是不变的;结合擦除,事情就不太合适,正如克里斯的答案中的例子所说明的那样。

但是,我认为可以放宽规范以允许创建通用数组 - 那里真的没有问题。危险来自上行投掷阵列;此时编译器警告就足够了。

实际上,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);

推荐