Стирание типа при реализации 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

Но сначала выполняется проверка типов, все происходит позже