Короткий ответ
При компиляции дженерик-информация стирается (type erasure): компилятор проверяет типы, а затем убирает их из байт-кода. Поэтому List<String> и List<Integer> после компиляции — один и тот же класс List: узнать параметр типа в рантайме нельзя. Raw type — параметризованный тип без параметра (List вместо List<String>): он оставлен для совместимости со старым кодом, но типобезопасность теряется.
Как это работает подробнее
Переменная типа T заменяется своей левой границей: без указанной границы — Object, для <T extends Animal> — Animal. Параметризованный тип превращается в сырой List, а не в List<Object>. Стирание введено ради обратной совместимости: код, написанный до Java 5, должен был продолжать работать и корректно взаимодействовать с новым.
Из стирания вытекают следствия: нельзя создать массив параметризованного типа (new List<String>[1] запрещено), нельзя проверить параметр типа в рантайме; чтобы передать тип в метод, используется дополнительный аргумент Class<T>.
- Raw types: компилятор выдаёт только предупреждение (unchecked warning), а не ошибку — небезопасный код проходит сборку.
- Перегрузка методов, различающихся только параметром типа, невозможна: после стирания сигнатуры совпадают.
Пример кода
import java.util.ArrayList;
import java.util.List;
public class ErasureDemo {
public static void main(String[] args) {
List<String> strings = new ArrayList<>();
List<Integer> numbers = new ArrayList<>();
// после компиляции это один и тот же класс List
System.out.println(strings.getClass() == numbers.getClass());
List raw = new ArrayList(); // raw type: только предупреждение
raw.add(42);
raw.add("java");
try {
String s = (String) raw.get(0);
} catch (ClassCastException e) {
System.out.println("ClassCastException: типобезопасность потеряна");
}
}
}Что использовать на практике
- Raw types не используйте в новом коде — только при вынужденной работе с легаси-кодом до Java 5.
- Если фактический тип нужен в рантайме, передавайте его явно — аргументом
Class<T>. - Помните: гарантии типов от дженериков действуют только на этапе компиляции.
Подводные камни
getClass()не различитList<String>иList<Integer>— параметр типа после компиляции недоступен.- Массив параметризованного типа (
new List<String>[1]) не скомпилируется. - Методы, отличающиеся только параметром типа, в одном классе объявить нельзя — ошибка из-за одинаковой erasure.
Как отвечать на собеседовании
- Каркас: скажите, что типы-параметры существуют только до компиляции → объясните, что
Tзаменяется левой границей (Object по умолчанию) → назовите причину — совместимость с Java до версии 5 → определите raw types и их риск → приведите следствие: запрет массивов параметризованных типов или одинаковые сигнатуры после стирания.