Что такое raw types и почему их следует избегать
Raw type — это дженерик-класс без type arguments:
🟢 Junior Level
Raw type — это использование дженерик-класса без указания типа.
// ✅ Параметризированный тип
List<String> list = new ArrayList<>();
list.add("Hello");
String s = list.get(0); // без cast
// ❌ Raw type — без типа
List list = new ArrayList(); // raw type!
list.add("Hello");
list.add(42); // компилятор не ругается, но это баг!
String s = (String) list.get(0); // нужен cast
String s2 = (String) list.get(1); // ClassCastException!
Почему стоит избегать:
- ❌ Нет проверки типов на компиляции
- ❌ Нужен cast
- ❌ Риск
ClassCastExceptionв runtime
🟡 Middle Level
Что такое raw type
Raw type — это дженерик-класс без type arguments:
// Дженерик-класс
public class Box<T> {
private T value;
public void set(T value) { this.value = value; }
public T get() { return value; }
}
// Параметризированное использование
Box<String> stringBox = new Box<>(); // ✅
// Raw type
Box rawBox = new Box(); // ❌ warning: raw type
Что происходит:
Box rawBox = new Box();
rawBox.set("Hello");
rawBox.set(42); // компилятор не ругается!
// После type erasure:
Box rawBox = new Box();
rawBox.set("Hello");
rawBox.set(Integer.valueOf(42)); // всё сохраняется
// При чтении — ClassCastException
String s = (String) rawBox.get(); // OK
Integer i = (Integer) rawBox.get(); // OK
String s2 = (String) rawBox.get(); // ClassCastException!
Типичные ошибки
- Legacy code совместимость: ```java // Старый код до Java 5 List list = new ArrayList(); list.add(“Hello”);
// Новый код
List
2. **Смешивание raw и parameterized:**
```java
Map<String, List<String>> map = new HashMap<>();
Map rawMap = map; // raw type — теряется безопасность
rawMap.put("key", 42); // компилятор не ругается
// Потом ClassCastException
List<String> list = map.get("key"); // ClassCastException!
Практическое применение
Единственный случай raw type — getClass():
// getClass() возвращает Class<? extends |X|>, не raw type.
// Присваивание в Class — unchecked conversion, не intended design.
Class<?> clazz = "Hello".getClass();
Class rawClass = "Hello".getClass(); // тоже OK, но unchecked conversion
// Но лучше использовать wildcard
Class<?> better = "Hello".getClass();
Когда raw types допустимы
- Совместимость с pre-Java 5 библиотеками
- Определённые reflection сценарии
- Class literals:
Class.class(хотя лучшеClass<?>)
🔴 Senior Level
Internal Implementation
Type erasure с raw types:
// Raw type — все type checks отключены
List<String> typed = new ArrayList<>();
List raw = typed;
raw.add(42); // компилятор пропускает!
raw.add(true); // компилятор пропускает!
// Но type erasure сохраняет данные как Object
// raw = typed после erasure — один и тот же объект
Unchecked warnings:
// Компилятор выдаёт warnings:
// warning: [unchecked] unchecked call to add(E) as a member of raw type List
raw.add(42);
// warning: [unchecked] unchecked conversion
List<String> list = raw;
// warning: [rawtypes] raw type
Box box = new Box();
Архитектурные Trade-offs
Raw types vs параметризированные:
| Raw types | Parameterized |
|---|---|
| Нет проверки типов | Compile-time проверка |
| Риск ClassCastException | Type-safe |
| Legacy совместимость | Современный код |
| Без cast при access | Cast не нужен |
Edge Cases
1. Raw type в наследовании:
public class Node<T> {
private T data;
public T getData() { return data; }
}
public class RawNode extends Node { // raw type!
@Override
public Object getData() { return super.getData(); }
}
// После erasure — bridge methods работают корректно
// Но теряется type safety
2. Raw type и generic методы:
public class Util {
public static <T> T identity(T t) { return t; }
}
// Вызов через raw type
Util u = new Util(); // raw type
Object result = u.identity("Hello"); // T выводится как Object
// Правильно
Object result2 = Util.identity("Hello"); // T = String
3. Raw type в массивах:
// Массивы generic классов — raw type
Box<String>[] boxes = new Box[10]; // warning: raw type
// Лучше
List<Box<String>> boxes = new ArrayList<>();
Производительность
Raw types vs parameterized:
- Runtime: одинаково (type erasure)
- Compile time: raw types быстрее (нет проверки)
- Безопасность: parameterized значительно безопаснее
Производительность одинакова, но raw types risky
Production Experience
Legacy migration:
// До Java 5
public class OldDao {
public List findAll() {
return jdbcTemplate.query("SELECT * FROM users", ...);
}
}
// После миграции
public class NewDao {
public List<User> findAll() {
return jdbcTemplate.query("SELECT * FROM users", ...);
}
}
// Transitional period — raw types warnings
List users = oldDao.findAll(); // raw type
List<User> typedUsers = newDao.findAll(); // parameterized
@SuppressWarnings(“rawtypes”):
// Только когда unavoidable
@SuppressWarnings("rawtypes")
public void legacyInterop() {
List rawList = legacyMethod();
// ...
}
// ❌ Не подавляйте warnings без причины
// ✅ Фиксите root cause
Best Practices
// ✅ Всегда используйте type arguments
List<String> list = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();
// ✅ Diamond operator (Java 7+)
List<String> list = new ArrayList<>(); // не new ArrayList<String>()
// ✅ Wildcards когда тип неизвестен
public void process(List<?> list) { }
// ⚠️ Raw types только для legacy кода
// ⚠️ Подавляйте warnings только когда unavoidable
// ❌ Raw types в новом коде
// ❌ Смешивание raw и parameterized
// ❌ Игнорирование unchecked warnings
🎯 Шпаргалка для интервью
Обязательно знать:
- Raw type — использование дженерик-класса без type argument:
List list = new ArrayList() - Raw types отключают compile-time проверку типов — риск ClassCastException в runtime
- Смешивание raw и parameterized типов заражает всю цепочку unchecked warnings
- Единственный допустимый raw type — legacy код и определённые reflection сценарии
- После type erasure raw и parameterized — один и тот же bytecode
@SuppressWarnings("rawtypes")— только когда unavoidable, не для подавления без причины
Частые уточняющие вопросы:
- Когда raw types допустимы? — Legacy pre-Java 5 библиотеки, некоторые reflection сценарии
- Что будет при смешивании raw и parameterized? — Unchecked warnings, потеря type safety
- Raw type vs wildcard <?>? — Raw отключает все проверки, wildcard сохраняет проверки
- Можно ли создать массив generic классов? — Да, но будет raw type:
new Box[10]
Красные флаги (НЕ говорить):
- ❌ “Raw types быстрее parameterized” — Runtime производительность одинакова
- ❌ “Raw types — нормальная практика в новом коде” — Всегда используйте type arguments
- ❌ “Raw type — это то же самое что <?>” — Raw отключает проверки, wildcard сохраняет
- ❌ “SuppressWarnings решает проблему” — Warning остаётся, нужно фиксить root cause
Связанные темы:
- [[11. Что такое дженерики (Generics) в Java]]
- [[12. В чём преимущества использования дженериков]]
- [[13. Что такое type erasure (стирание типов)]]
- [[21. В чём разница между List<?> и List