【问题标题】:Why couldn't arrays of generics be implemented in Java? [duplicate]为什么不能用 Java 实现泛型数组? [复制]
【发布时间】:2017-10-19 00:31:45
【问题描述】:

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[],使用编译器为类型擦除而提取的相同"shtick"

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

问题比那个答案中指出的更深,所以进一步 需要进行调查。正如你所说,类型信息被删除并进入 编译后的代码我们在两种泛型类型之间没有区别——我们只 have 是基本类型 - 那么为什么要使用 T[] - 编译为 Object[]。在这种情况下 一切都会好起来的——数组会记住它是用 对象类型,并允许保存所有类型。然而对于我来说真正的 问题是数组是协变的,这意味着 Animal[] 可以是 分配给Object[]。另一方面,泛型不是ArrayList&lt;Animal&gt; 不能分配给ArrayList&lt;Object&gt;

因为这个逻辑是有缺陷的!

这里有两个过程。

  1. 编译器对ArrayList&lt;String&gt;强制“人工”不变性。

  2. 编译器将Object 转换为T

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

【问题讨论】:

    标签: java arrays generics


    【解决方案1】:

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

    数组在运行时强制执行:

    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,这通常会导致您的代码在某些不可预测的地方或时间失败,从而难以诊断和修复。因此发出警告。

    【讨论】:

      【解决方案2】:

      这段代码抛出异常:

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

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

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

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

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

      可以想象,它们可能完全改变了数组的工作方式,因此它们只有编译时子类型,并且在运行时都只是Object[]。不过,这将是一个相当大的变化,并且会破坏旧代码。

      就个人而言,我认为他们应该只添加一个通用的class Array&lt;E&gt;,它的用途与通用数组相同。一开始就真的需要一个是非常罕见的。

      【讨论】:

        猜你喜欢
        • 2013-09-20
        • 1970-01-01
        • 2013-09-03
        • 1970-01-01
        • 2018-12-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-04-10
        相关资源
        最近更新 更多