Що таке 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