【问题标题】:compilation error while trying to define a generic array [duplicate]尝试定义通用数组时出现编译错误[重复]
【发布时间】:2014-07-02 11:50:49
【问题描述】:
public class foo<T> {

    protected T[] arr;

    public foo() {
        T[] f = new T[5];
    }
}

我有两个问题:

“无法创建 T 的通用数组”,我收到 T[] f = new T[5]; 的此错误。为什么我不能创建一个泛型数组?

如果不可能,为什么我可以声明一个引用到一个泛型数组?我没有收到声明的编译错误:protected T[] arr;

【问题讨论】:

  • 当 T 是 erased 时,JVM 应该如何知道 T 在运行时是什么类型?
  • 如果是这样,那和List&lt;T&gt;有什么区别?
  • @AngryOliver 最大的区别是数组知道它们的底层类型而泛型类型不知道。

标签: java generics types compilation


【解决方案1】:

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

你需要知道几件事:

  1. 通用类型在运行时为erased,因此T将编译为Object,或者如果&lt;T extends SomeType&gt;将变为SomeType
  2. 为了确保运行时的安全性,数组需要记住它可以容纳什么类型的元素。换句话说,它记住了这个

    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

【讨论】:

  • 首先感谢您的详细解释,但List&lt;Ojbect&gt; 不是同样有风险吗?
  • @AngryOliver 如果您使用List&lt;Object&gt;,那么您明确表示 list 可以存储任何类型的对象(因为所有类都隐式扩展 Object)所以我在这里看不到任何问题。如果您想询问List&lt;Fruit&gt; 将引用List&lt;Apple&gt;,那么Java 通过简单地禁止List&lt;Fruit&gt; 引用List&lt;Apple&gt; 解决了这个问题。看看Is List&lt;Dog&gt; a subclass of List&lt;Animal&gt;?
  • 但是你写了关于Object[] f = new Object[5];:这意味着数组可以让你存储任何类型的元素,这似乎风险太大,所以Java创建者没有让编译器接受这种代码.那么,为什么List&lt;Object&gt; 没有风险,而数组有风险呢?
  • @AngryOliver List&lt;Object&gt; 也是有风险的,这就是为什么你不能使用 List&lt;Object&gt; 作为对 List&lt;String&gt; 的引用,就像你可以使用数组 Object[] array = new String[5] 一样,因为数组给了你运行时保护(他们检查可以放入其中的类型)而泛型类型不能(它们被删除),因此 Java 创建者决定禁止它。 List&lt;Object&gt; 只能引用实际对象列表List&lt;Object&gt; list = new ArrayList&lt;Object&gt;(),就像List&lt;Fruit&gt; 只能引用水果列表new ArrayList&lt;Fruit&gt; 但不能引用new ArrayList&lt;Apple&gt;
  • @AngryOliver 因为通过List&lt;Fruit&gt; 您可以将香蕉也添加到 Apples 列表中,这一次尝试将这个不正确的元素放入列表时不会发生异常,因为实际上支持列表由可以接受任何类型元素的 Object 数组组成。
猜你喜欢
  • 1970-01-01
  • 2013-02-18
  • 1970-01-01
  • 2021-12-03
  • 1970-01-01
  • 1970-01-01
  • 2021-12-28
  • 2013-03-04
  • 1970-01-01
相关资源
最近更新 更多