Raw type
Определение
Raw type — использование обобщённого класса или интерфейса без аргумента типа:
List raw = new ArrayList(); // raw type
List<String> names = new ArrayList<>(); // параметризованный типТакая форма сохранена ради совместимости с Java-кодом, написанным до появления generics. В новом коде raw type почти всегда означает потерю проверки типов на этапе компиляции.
Как теряется безопасность
List<String> names = new ArrayList<>();
List raw = names;
raw.add(42); // warning, но компилируется
String first = names.get(0); // ClassCastExceptionОбъект физически один и тот же. Через raw-ссылку в коллекцию попало значение другого типа — это пример загрязнения кучи (heap pollution). Ошибка проявилась далеко от опасной записи, при сгенерированном компилятором приведении.
Компилятор предупреждает:
unchecked call
unchecked assignment
unchecked castТакое предупреждение нельзя глушить автоматически: сначала нужно доказать, что операция действительно безопасна.
Raw type и wildcard — не одно и то же
List raw = new ArrayList<String>();
List<?> unknown = new ArrayList<String>();Listотключает значительную часть проверки типов.List<?>означает «список некоторого неизвестного типа» и остаётся типобезопасным.
В List<?> нельзя добавить произвольный объект, кроме null, потому что компилятор не знает фактический тип элемента. Зато читать можно как Object.
int sizeOf(List<?> values) {
return values.size();
}Для метода, которому не важен тип элементов, wildcard — правильный выбор.
Где raw type ещё встречается
Из-за стирания типов допустимы class literal и проверка только сырой части типа:
Class<List> listClass = List.class;
if (value instanceof List<?> list) {
System.out.println(list.size());
}Проверить value instanceof List<String> нельзя: аргумент String недоступен во время выполнения.
На границе со старой библиотекой raw type иногда неизбежен. Тогда опасный участок стоит локализовать, проверить каждый элемент и вернуть новую типизированную коллекцию без общего alias:
List<String> readLegacyNames() {
List values = legacyApi.load(); // raw type остаётся только на границе
List<String> result = new ArrayList<>();
for (Object value : values) {
result.add(String.class.cast(value));
}
return List.copyOf(result);
}Если без unchecked cast не обойтись, подавление предупреждения должно быть узким и иметь понятное доказательство безопасности.
Ловушки
- Использовать raw type вместо
<?>. - Считать warning несущественным, хотя он переносит ошибку из компиляции во время выполнения.
- Ставить
@SuppressWarnings("unchecked")на весь класс. - Ожидать, что JVM различает
List<String>иList<Integer>. - Делать непроверяемое приведение по одному лишь
instanceof List<?>.
Вопросы на собеседовании
- Что такое raw type и зачем он существует?
- Чем
Listотличается отList<?>? - Что называют heap pollution?
- Почему нельзя написать
instanceof List<String>? - Когда допустимо подавить unchecked warning?
Связанные заметки
Обобщения generics · Параметризованные классы Generics · List · Преобразование типов Java