Короткий ответ
При компиляции дженерик-информация стирается (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: типобезопасность потеряна");
}
}
}