Java不允许我们这么做的原因是什么

private T[] elements = new T[initialCapacity];

我可以理解。net不允许我们这样做,因为在。net中,值类型在运行时可以有不同的大小,但在Java中,所有类型的T都将是对象引用,因此具有相同的大小(如果我错了请纠正我)。

原因是什么?


当前回答

正如其他人已经提到的,你当然可以通过一些技巧来创造。

但不建议这样做。

因为类型擦除,更重要的是数组中的协方差,它允许子类型数组被赋给超类型数组,这迫使你在试图取回值时使用显式类型强制转换,导致运行时ClassCastException,这是泛型试图消除的主要目标之一:在编译时进行更强的类型检查。

Object[] stringArray = { "hi", "me" };
stringArray[1] = 1;
String aString = (String) stringArray[1]; // boom! the TypeCastException

一个更直接的例子可以在Effective Java: Item 25中找到。


协方差:如果S是T的子类型,则类型S[]的数组是T[]的子类型

其他回答

这是不可能的,因为Java完全在编译器级别上实现了泛型,并且每个类只生成一个类文件。 这叫做类型擦除。

在运行时,已编译的类需要用相同的字节码处理它的所有使用。因此,new T[capacity]将完全不知道需要实例化什么类型。

引用:

Arrays of generic types are not allowed because they're not sound. The problem is due to the interaction of Java arrays, which are not statically sound but are dynamically checked, with generics, which are statically sound and not dynamically checked. Here is how you could exploit the loophole: class Box<T> { final T x; Box(T x) { this.x = x; } } class Loophole { public static void main(String[] args) { Box<String>[] bsa = new Box<String>[3]; Object[] oa = bsa; oa[0] = new Box<Integer>(3); // error not caught by array store check String s = bsa[0].x; // BOOM! } } We had proposed to resolve this problem using statically safe arrays (aka Variance) bute that was rejected for Tiger. -- gafter

(我相信是尼尔·盖特,但不确定)

在这里查看上下文:http://forums.sun.com/thread.jspa?threadID=457033&forumID=316

Oracle教程:

You cannot create arrays of parameterized types. For example, the following code does not compile: List<Integer>[] arrayOfLists = new List<Integer>[2]; // compile-time error The following code illustrates what happens when different types are inserted into an array: Object[] strings = new String[2]; strings[0] = "hi"; // OK strings[1] = 100; // An ArrayStoreException is thrown. If you try the same thing with a generic list, there would be a problem: Object[] stringLists = new List<String>[]; // compiler error, but pretend it's allowed stringLists[0] = new ArrayList<String>(); // OK stringLists[1] = new ArrayList<Integer>(); // An ArrayStoreException should be thrown, // but the runtime can't detect it. If arrays of parameterized lists were allowed, the previous code would fail to throw the desired ArrayStoreException.

对我来说,这听起来很软弱。我认为任何对泛型有充分理解的人,都完全可以理解,甚至期望,在这种情况下ArrayStoredException不会被抛出。

正如其他人已经提到的,你当然可以通过一些技巧来创造。

但不建议这样做。

因为类型擦除,更重要的是数组中的协方差,它允许子类型数组被赋给超类型数组,这迫使你在试图取回值时使用显式类型强制转换,导致运行时ClassCastException,这是泛型试图消除的主要目标之一:在编译时进行更强的类型检查。

Object[] stringArray = { "hi", "me" };
stringArray[1] = 1;
String aString = (String) stringArray[1]; // boom! the TypeCastException

一个更直接的例子可以在Effective Java: Item 25中找到。


协方差:如果S是T的子类型,则类型S[]的数组是T[]的子类型

如果我们不能实例化泛型数组,为什么语言有泛型数组类型?没有对象的类型有什么意义呢?

我能想到的唯一原因是varargs - foo(T…)。否则,它们可以完全删除泛型数组类型。(好吧,他们真的不需要为可变参数使用数组,因为在1.5之前不存在可变参数。这可能是另一个错误。)

所以这是一个谎言,你可以实例化泛型数组,通过varargs!

当然,泛型数组的问题仍然存在,例如。

static <T> T[] foo(T... args){
    return args;
}
static <T> T[] foo2(T a1, T a2){
    return foo(a1, a2);
}

public static void main(String[] args){
    String[] x2 = foo2("a", "b"); // heap pollution!
}

我们可以用这个例子来实际演示泛型数组的危险。

另一方面,我们已经使用泛型变参数10年了,现在还没有崩溃。所以我们可以说问题被夸大了;这没什么大不了的。如果允许显式的泛型数组创建,我们会到处都有bug;但我们已经习惯了擦除的问题,我们可以接受它。

我们可以用foo2来反驳这种说法,即规范让我们避免了他们声称让我们避免的问题。如果Sun在1.5上有更多的时间和资源,我相信他们可以达成一个更令人满意的解决方案。