在Java中实现ArrayList时键入擦除

时间:2014-03-07 20:08:35

标签: java object generics arraylist constructor

我正在Java Generics阅读这篇文章,并且提到ArrayList的构造函数看起来像这样:

class ArrayList<V> {
  private V[] backingArray;
  public ArrayList() {
    backingArray = (V[]) new Object[DEFAULT_SIZE]; 
  }
}

我无法理解编译器的类型擦除和类型检查是如何解释的那样。我得到的一点是,type参数转换为Object类型。

我会把它想象成(用V替换所有Object),但这肯定是错的。

class ArrayList<Object> {
      private Object[] backingArray;
      public ArrayList() {
        backingArray = (Object[]) new Object[DEFAULT_SIZE]; 
      }
}

它究竟是如何转换为Object类型但仍保留V的类型安全性? 当我有ArrayList<String>ArrayList<Integer>时,每个都有两个不同的类?如果不存在,StringInteger的类型信息存储在哪里?

3 个答案:

答案 0 :(得分:8)

您的类型删除版本不正确。类型参数声明不会被删除到Object,但只会删除它的用法。更具体地说:

  • 删除泛型类型是其对应的原始类型。因此,对于ArrayList<V>,它只是ArrayList
  • 类型参数的擦除是其最左边界限。
  • 所有类型参数都被删除了。类型参数是在实例化泛型类时使用的参数。因此,ArrayList<Integer>将替换为ArrayList

因此,正确的删除版本将是:

class ArrayList {
    private Object[] backingArray;
    public ArrayList() {
      backingArray = (Object[]) new Object[DEFAULT_SIZE]; 
    }
}
  

当我有ArrayList和ArrayList时,每个都有两个不同的类?

不,情况绝对不是这样。编译器仅生成泛型类型或方法的一个字节代码表示,并将泛型类型或方法的所有实例映射到唯一表示。

  

如果没有存储String和Integer的类型信息的位置?

当编译器执行类型擦除时,它会根据一些预定义的规则删除所有类型信息,偶尔会添加所谓的桥接方法,并添加所需的所有类型转换。

因此,例如,ArrayList<Integer>ArrayList<String>的以下用法:

ArrayList<Integer> list = new ArrayList<Integer>();
list.add(1);
int value = list.get(0);

ArrayList<String> list2 = new ArrayList<String>();
list.add("A");
String value2 = list.get(0);

将转换为有点像这样:

ArrayList list = new ArrayList();
list.add(1);
int value = (Integer) list.get(0);

ArrayList list2 = new ArrayList();
list.add("A");
String value2 = (String) list.get(0);

进一步阅读:

答案 1 :(得分:3)

你的第二个例子是不正确的。类型擦除并不意味着全局将所有内容都转换为Object。正如你猜测的那样,这几乎没有任何意义。相反,什么类型的擦除做(字面上)以下(借用Jon Skeet):

List<String> list = new ArrayList<String>();
list.add("Hi");
String x = list.get(0);

这段代码被翻译成:

List list = new ArrayList();
list.add("Hi");
String x = (String) list.get(0);

注意投射到String,而不仅仅是一个香草Object。类型擦除“擦除”泛型类型并将其中的所有对象强制转换为T。这是一种添加一些编译时用户友好性而不会产生运行时成本的聪明方法。然而,正如文章所声称的那样,这并非没有妥协。

考虑以下示例:

ArrayList<Integer> li = new ArrayList<Integer>();
ArrayList<Float> lf = new ArrayList<Float>();

它可能看起来不直观(或正确),但li.getClass() == lf.getClass()将评估为真。

答案 2 :(得分:0)

好问题。首先完成类型检查。如果所有内容都编译(即,在提供编译类型安全性之后),则会发生类型擦除。

同样,很多事情都是类型擦除的一部分,其中包括: -

1)Adding casts 
2) creating bridge methods

但是先完成类型检查,所有事情都会在稍后进行