为什么我不能创建一个泛型数组?
你需要知道几件事:
- 通用类型在运行时为erased,因此
T将编译为Object,或者如果<T extends SomeType>将变为SomeType,
-
为了确保运行时的安全性,数组需要记住它可以容纳什么类型的元素。换句话说,它记住了这个
new ElementType[size];
^^^^^^^^^^^
在运行时,您可以尝试使用不同类型的引用来保存某个对象。例如,如果您有课程
abstract class Fruit{}
class Apple extends Fruit{}
class Banana extends Fruit{}
你可以使用
Fruit[] fruits = new Apple[10];
因为fruits 被视为Fruit 的数组,编译器会让你写
fruits[0] = new Apple();
还有
fruits[0] = new Banana();
因为fruits 也可以保存Banana 数组(出于同样的原因,它现在可以保存Apple 数组),但是正如您所见,Banana 不是Apple,所以您需要一种机制来阻止您使用不适当的元素破坏您的数组。
因此,在将元素放入数组时,它会执行测试以检查元素类型是否相同或创建数组时使用的类的子类型。
此机制不适用于
T[] f = new T[5];
因为在运行时这段代码看起来像
Object[] f = new Object[5];
这意味着数组可以让你存储 any 类型的元素,这似乎风险太大,因此 Java 创建者不会让编译器接受这种代码。
为什么我可以声明对通用数组的引用?
出于几个原因,但主要是因为有一种方法可以创建T 类型的数组,几乎不需要反射。
如果你使用
T[] t = (T[])Array.newInstance(componentType, size);
您将创建componentType 类型的数组。 componentType 必须是 Class 的实例,它代表通用 T 类型。换句话说,我们的代码看起来像
class foo<T> {
protected T[] arr;
@SuppressWarnings("unchecked")
public foo(Class<T> componentType) {
arr = (T[]) Array.newInstance(componentType, 5);
}
}
你可以像这样使用它
foo<String> f = new foo<String>(String.class);
// ^^^^^^^^^^^^ class literal