Java 1.6:创建一个List<T>数组

33

为什么我不能创建一个 List 数组?

List<String>[] nav = new List<String>[] { new ArrayList<String>() };

Eclipse 提示 "无法创建 List 的泛型数组"

或者

ArrayList<String>[] nav = new ArrayList<String>[] { new ArrayList<String>() };

Eclipse 提示 "无法创建 ArrayList 的泛型数组"

或者

List<String>[] getListsOfStrings() {
    List<String> groupA = new ArrayList<String>();
    List<String> groupB = new ArrayList<String>();
    return new List<String>[] { groupA, groupB };
}

但我可以这样做:

List[] getLists() {
    return new List[] { new ArrayList(), new ArrayList() };
}

Eclipse提示List和ArrayList是“原始类型”,但实际上可以编译通过...

看起来很简单,为什么不起作用呢?


2
可能是Java泛型数组语法的重复问题。 - Joachim Sauer
4个回答

29

好的,泛型教程 给出了你问题的答案。

数组对象的组成类型可能不是类型变量或参数化类型,除非它是(无界)通配符类型。您可以声明元素类型为类型变量或参数化类型的数组类型,但不能声明数组对象

这确实很烦人。这个限制是必要的,以避免类似以下情况的发生:

// Not really allowed.
List<String>[] lsa = new List<String>[10];
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
// Unsound, but passes run time store check
oa[1] = li;

// Run-time error: ClassCastException.
String s = lsa[1].get(0);

如果允许参数化类型的数组, 先前的示例将编译而不会出现任何未经检查的警告,但在运行时失败。 泛型的主要设计目标是类型安全。


9
如果允许参数化类型的数组,则上述示例将会编译通过而没有未经检查的警告,但在运行时将失败。这不是原因。Object[] sa = new String[10]; oa[1] = new Integer(3);也可以"编译通过而没有未经检查的警告,但在运行时却失败了"。更重要的是它应该在存储的时候就会失败,抛出一个ArrayStoreException(因此数组是在运行时检查)。但是泛型无法通过运行时检查,这就是为什么创建参数化类型的数组是被禁止的原因。 - newacct

14

一般情况下,你无法创建泛型类型的数组。

原因是JVM在运行时没有办法检查只有正确的对象被放入其中(使用ArrayStoreExceptions),因为List<String>和List<Integer>之间的区别在运行时不存在。

当然,你可以通过使用裸类型List或未绑定通配符类型List<?>来欺骗编译器,然后将其强制转换(使用不受检查的强制转换)为List<String>。但是,这时你就需要自己负责只把List<String>放进去而不是其他列表。


1

没有确切的答案,但有一个提示:

最后一个例子出现了原始类型警告,因为您省略了列表的类型化;通常更好(类型安全)的方法是指定列表中包含哪些对象类型,这在之前的示例中已经做到了(List<String>而不是List)。

使用数组不是最佳实践,因为它们的使用大多数时候都会出错;使用集合类(List、Set、Map等)可以使用类型化和方便处理其内容的方法;只需查看Collections类的静态方法即可。

因此,只需使用上一个答案的示例即可。


但是你不能创建一个 List<String> 的数组 - 这就是问题的重点(也是为什么最后一个示例被使用的原因)。 - Paŭlo Ebermann

0
另一种解决方案是扩展LinkedList<String>(或ArrayList<String>等),然后创建子类的数组。
private static class StringList extends LinkedList<String> {}

public static void main(String[] args)
{
    StringList[] strings = new StringList[2];
    strings[0] = new StringList();
    strings[1] = new StringList();
    strings[0].add("Test 1");
    strings[0].add("Test 2");
    strings[1].add("Test 3");
    strings[1].add("Test 4");
    System.out.println(strings[0]);
    System.out.println(strings[1]);
}

1
你可以这样做,但这违背了泛型的初衷。 - Leigh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接