PECS = Producer Extends, Consumer Super. Collection cấp dữ liệu cho bạn (producer) thì dùng ? extends T; collection nhận dữ liệu bạn đưa vào (consumer) thì dùng ? super T.
static double sum(List<? extends Number> src) { // producer: chỉ đọc
double total = 0;
for (Number n : src) total += n.doubleValue();
// src.add(1); // compile error
return total;
}
static void fill(List<? super Integer> dest) { // consumer: chỉ ghi
dest.add(1);
Object o = dest.get(0); // đọc ra chỉ được Object
}Lý do compiler cấm:
- List<? extends Number> có thể đang trỏ tới List<Long>, nên add một Integer sẽ phá kiểu → chỉ add được null.
- List<? super Integer> có thể là List<Object>, nên phần tử lấy ra chỉ chắc chắn là Object.
Vì sao cần: List<Integer> không phải subtype của List<Number> (generics là invariant). Wildcard là cách khôi phục tính linh hoạt mà vẫn an toàn kiểu. Ví dụ chuẩn trong JDK là Collections.copy(List<? super T> dest, List<? extends T> src) — đúng một producer và một consumer trong cùng chữ ký.
Hai quy tắc kèm theo: tham số vừa đọc vừa ghi thì không dùng wildcard; và không dùng wildcard làm kiểu trả về vì nó ép người gọi phải xử lý wildcard theo.