我正在读这篇文章 Java泛型 并且在那里提到了一个构造函数 ArrayList
看起来有点像这样:
class ArrayList<V> {
private V[] backingArray;
public ArrayList() {
backingArray = (V[]) new Object[DEFAULT_SIZE];
}
}
我无法理解编译器的类型擦除和类型检查是如何解释的那样。我得到的一点是类型参数转换为 Object
类型。
我会想象它(取代所有 V
同 Object
),但这绝对是错误的。
class ArrayList<Object> {
private Object[] backingArray;
public ArrayList() {
backingArray = (Object[]) new Object[DEFAULT_SIZE];
}
}
究竟是如何转变为 Object
类型但仍保留类型安全 V
?
当我有 ArrayList<String>
和 ArrayList<Integer>
每个都有两个不同的类?如果没有,那里的类型信息在哪里 String
和 Integer
被储存了?
您的类型擦除版本不正确。类型参数声明不会被删除 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);
进一步阅读:
你的第二个例子不正确。类型擦除并不意味着全局地将一切都投射到 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()
将评估为真。
好问题。首先进行类型检查。如果所有内容都编译(即,在提供编译类型安全性之后),则会发生类型擦除。
同样,很多事情都是类型擦除的一部分,其中包括: -
1)Adding casts
2) creating bridge methods
但是首先进行类型检查,一切都在以后进行