Do Java Generics là invariant: ArrayList<String> KHÔNG phải subtype của ArrayList<Object>. Ép kiểu trực tiếp báo lỗi compile.
Cách 1: Wildcard ? (Type erasure trick)
Dùng ArrayList<?> làm trung gian (raw type an toàn hơn).
ArrayList<Object> objList = new ArrayList<>();
objList.add("apples"); objList.add("mangoes");
// Object -> String (Unsafe tại runtime nếu objList chứa non-String!)
ArrayList<String> strList = (ArrayList<String>)(ArrayList<?>) objList;
// Hoặc an toàn hơn: copy
ArrayList<String> safeStrList = new ArrayList<>(objList); // Warning unchecked
Cách 2: Copy qua Constructor (Khuyên dùng - An toàn)
// String -> Object (Luôn an toàn: String IS-A Object)
ArrayList<String> strList = new ArrayList<>();
strList.add("apples");
ArrayList<Object> objList = new ArrayList<>(strList); // OK, implicit upcast
// Object -> String (CẦN KIỂM TRA runtime)
ArrayList<Object> objList2 = new ArrayList<>();
objList2.add("apples");
objList2.add(123); // Integer!
// Cách an toàn: filter & copy
ArrayList<String> strList2 = objList2.stream()
.filter(String.class::isInstance)
.map(String.class::cast)
.collect(Collectors.toCollection(ArrayList::new));
Tóm tắt quy tắc
| Chuyển đổi | An toàn? | Cách làm |
|---|---|---|
ArrayList<String> → ArrayList<Object> | ✅ An toàn | new ArrayList<>(stringList) |
ArrayList<Object> → ArrayList<String> | ❌ Unsafe | Phải check runtime hoặc dùng wildcard cast (rủi ro ClassCastException) |
Tại sao Generic invariant?
// Nếu cho phép: List
Khuyên dùng: Thiết kế API dùng List<T> thay vì ArrayList<T>. Dùng List<? extends T> (producer) hoặc List<? super T> (consumer) cho flexibility (PECS principle).