为什么不能用Java实现泛型数组呢?

时间:2017-10-19 00:31:46

标签: java arrays generics

Java使用类型擦除

据我所知,new ArrayList<String>()被转换为原始类型,并且使用了大量的语法糖来假装Object的这个ArrayList就像String的ArrayList。 Java将此称为类型擦除。

例如

这是我用Java写的:

public static void main(String[] args) {
    ArrayList<String> stringList = new ArrayList<>();
    stringList.add("foo");
    String s = stringList.get(0);
}

当我反编译字节码时,我得到了这个:

public static void main(String[] args) {
    ArrayList<String> stringList = new ArrayList();
    stringList.add("foo");
    String s = (String)stringList.get(0);
} 

因此

为什么不能new T[]自动转换为(T[]) new Object[] ,使用相同的&#34; shtick&#34; 编译器拉取类型擦除?

请不要向我推荐这个问题:What's the reason I can't create generic array types in Java?特别是这条评论:

  

这个问题比答案更深刻,更进一步   需要调查。正如你所说,类型信息被删除了   编译代码我们在两个泛型类型之间没有区别 - 我们都是   有基类型 - 为什么T[] - 编译为Object[]。在这种情况下   一切都会很好 - 数组会记住它是用它创建的   对象类型并将保存所有类型。但对我来说真实   问题是数组是协变的意味着Animal[]可以   分配给Object[]。另一方面,泛型不是ArrayList<Animal>   无法分配到ArrayList<Object>

因为这个逻辑存在缺陷!

这里有两个进程。

  1. 编译器对ArrayList<String>强制实施&#34;仿真&#34; 不变性。

  2. 编译器将Object转换为T

  3. 同样,为什么不能使用形成所有泛型的简单语法糖在Java中实现泛型数组,同时保持数组的正常协方差?

2 个答案:

答案 0 :(得分:8)

可以通过两种方式强制执行类型安全:在运行时或编译时。

Arrays在运行时强制执行它:

Object[] array = new Integer[1];
array[0] = "";
// ArrayStoreException!

泛型在编译时强制执行:

List<Object> list1 = new ArrayList<Integer>();
// does not compile!

List<? extends Object> list2 = new ArrayList<Integer>();
list2.add(1);
// does not compile!

因为T[]是一个数组,所以它必须是协变的,但是由于类型擦除,在运行时没有办法检查类型,如下例所示:

Object[] array = (T[])new Object[1];
array[0] = 1;

由于在运行时array实际 类型为Object[],因此无论T恰好是什么,此代码都将无错误地编译和运行。这导致heap pollution,这通常会导致您的代码在某个不可预测的地方或时间失败,从而难以诊断和修复。因此警告。

答案 1 :(得分:4)

此代码抛出异常:

static <T> T[] newArray() {
    return (T[]) new Object[0];
}
static {
    // throws ClassCastException,
    // because Object[] is not a String[].
    String[] a = newArray();
}

这是因为String[]Object[]在运行时实际上有不同的类型。 (这与泛型不同:例如,List<String>List<Object>具有相同的类。)

根据您的建议,方法newArray将如下所示:

static <T> T[] newArray() {
    return new T[0];
}

这显然不是错的,但确实如此。我认为这种变化只会导致更多的实际收益混乱。

可以想象,他们可以完全改变数组的工作方式,因此他们只有编译时子类型,并且在运行时只是Object[]。不过,这本来是一个非常重大的变化,并且会打破旧代码。

就个人而言,我认为他们应该只添加一个通用class Array<E>,它与通用数组的用途相同。实际上首先需要一个是非常罕见的。