Стирание типов в Java – о чем это и для чего?


Стирание типов (Type Erasure) — это механизм в языке Java, связанный с обобщениями (generics). Он был введен для того, чтобы обеспечить совместимость с кодом, написанным до появления обобщений, и сводится к тому, что информация о типах обобщений удаляется на этапе компиляции. В результате обобщенные типы (generics) не существуют в байт-коде JVM, и все операции с ними происходят на уровне сырых типов (raw types).

Суть стирания типов

Когда вы используете обобщения в коде, на этапе компиляции компилятор проверяет правильность типов (type safety). Однако на этапе выполнения эта информация о типах больше не сохраняется. Это называется стиранием типов.

Пример:

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

После компиляции этот код превратится в нечто подобное:

List list = new ArrayList();
list.add("Hello");
String s = (String) list.get(0);

Как видно, на этапе выполнения компилятор заменяет все обобщенные типы на их сырые аналоги (например, List вместо List<String>). Проверки типов и приведения остаются на этапе компиляции, но информация о типах (в данном случае String) не сохраняется в байт-коде.

Для чего нужно стирание типов?

  1. Совместимость с предыдущими версиями Java: Обобщения были добавлены в Java 5, а стирание типов позволило сохранить обратную совместимость с кодом, написанным до этой версии. Это значит, что можно использовать старый код с новыми обобщенными коллекциями без проблем.
  2. Обеспечение безопасности типов на этапе компиляции: Обобщения позволяют компилятору проверять типы во время компиляции, предотвращая ошибки, связанные с неправильным приведением типов.

Как работает стирание типов?

  1. Замена обобщенных типов на сырые: Все параметры типов заменяются на их верхнюю границу. Если явная граница не указана, используется Object. Например:
   public class Box<T> {
       private T item;
   }

Превратится в:

   public class Box {
       private Object item;
   }
  1. Вставка приведения типов: Когда вы извлекаете элемент из обобщенной коллекции, компилятор автоматически вставляет приведение типов. Пример:
   List<String> list = new ArrayList<>();
   String item = list.get(0);  // На этапе выполнения добавляется приведение (String)
  1. Методы с ограниченными типами: Если метод использует ограниченные параметры типов (T extends SomeClass), то на этапе компиляции эти параметры заменяются на указанный ограниченный тип. Например:
   public <T extends Number> void printNumber(T number) {
       System.out.println(number);
   }

Превратится в:

   public void printNumber(Number number) {
       System.out.println(number);
   }

Ограничения, вызванные стиранием типов

  1. Невозможно создание обобщенных массивов:
   List<String>[] list = new ArrayList<String>[10];  // Ошибка компиляции

Это связано с тем, что информация о типах теряется, и компилятор не может гарантировать типобезопасность.

  1. Невозможно использовать оператор instanceof с параметризованными типами:
   if (obj instanceof List<String>) {  // Ошибка компиляции
       // ...
   }

После стирания типов не остается информации о типе String, поэтому такая проверка невозможна.

  1. Ограничения на перегрузку методов:
    Из-за стирания типов перегружать методы с разными параметрами обобщенных типов невозможно. Например:
   public void doSomething(List<String> list) { ... }
   public void doSomething(List<Integer> list) { ... }  // Ошибка компиляции

Заключение

Стирание типов — это важный аспект работы обобщений (generics) в Java, который обеспечивает совместимость с устаревшим кодом, сохраняя при этом проверку типов на этапе компиляции. Однако оно также накладывает определенные ограничения на использование обобщений, такие как невозможность создания обобщенных массивов и проверки типов во время выполнения.

,