Стирание типа при реализации ArrayList в Java
Я читал эту статью в Java Generics и там упоминается, что конструктор для 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
?
Ответы
Ответ 1
Утерянная версия вашего типа неверна. Объявление параметра типа не удаляется до Object
, но только его использование стирается. Более конкретно:
- Стирание родового типа является его соответствующим сырым типом. Итак, для
ArrayList<V>
это будет просто ArrayList
.
- Erasure параметра типа является самой левой границей.
- И все аргументы типа просто удаляются. Аргументы типа - это тот, который вы используете при создании экземпляра родового класса. Итак,
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);
Дальнейшее чтение:
Ответ 2
Ваш второй пример неверен. Стирание стилей не означает, что глобальное использование всего 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()
будет оцениваться как true.
Ответ 3
Хороший вопрос. Проверка типов выполняется сначала. Если все скомпилировано (то есть после обеспечения безопасности типа компиляции), тип стирания происходит.
Опять же, многие вещи происходят как часть стирания типа, которая включает в себя: -
1)Adding casts
2) creating bridge methods
Но сначала выполняется проверка типов, все происходит позже