Tôi đã chơi xung quanh với Generics và thấy rằng, trước sự ngạc nhiên của tôi, đoạn mã sau biên dịch:Tại sao không phải là loại an toàn java khi suy ra các loại mảng?
class A {}
class B extends A {}
class Generic<T> {
private T instance;
public Generic(T instance) {
this.instance = instance;
}
public T get(){ return instance; }
}
public class Main {
public static void main(String[] args) {
fArray(new B[1], new Generic<A>(new A())); // <-- No error here
}
public static <T> void fArray(T[] a, Generic<? extends T> b) {
a[0] = b.get();
}
}
Tôi mong chờ T
được suy ra B
. A
không mở rộng B
. Vậy tại sao trình biên dịch không phàn nàn về nó?
T
dường như được suy ra là Object
, vì tôi cũng có thể vượt qua một số Generic<Object>
.
Hơn nữa, khi thực sự chạy mã, nó ném một số ArrayStoreException
trên đường dây a[0] = b.get();
.
Tôi không sử dụng bất kỳ loại chung chung nào. Tôi cảm thấy ngoại lệ này có thể tránh được với lỗi thời gian biên dịch hoặc ít nhất là cảnh báo, nếu T
thực sự được phỏng đoán là B
.
Khi thử nghiệm thêm với List<...>
tương đương:
public static void main(String[] args) {
fList(new ArrayList<B>(), new Generic<A>(new A())); // <-- Error, as expected
}
public static <T> void fList(List<T> a, Generic<? extends T> b) {
a.add(b.get());
}
này sản xuất và lỗi:
The method fList(List<T>, Generic<? extends T>) in the type Main is not applicable for the arguments (ArrayList<B>, Generic<A>)
Như hiện các trường hợp chung chung hơn:
public static <T> void fList(List<? extends T> a, Generic<? extends T> b) {
a.add(b.get()); // <-- Error here
}
Các trình biên dịch nhận ra chính xác rằng ?
đầu tiên có thể tiếp tục xuống cấp bậc thừa kế hơn so với số thứ hai ?
.
ví dụ: Nếu số ?
đầu tiên là B
và số ?
thứ hai là A
thì đây không phải là loại an toàn.
Vậy tại sao ví dụ đầu tiên không tạo ra lỗi trình biên dịch tương tự? Nó chỉ là một sự giám sát? Hoặc là có một giới hạn kỹ thuật?
Cách duy nhất tôi có thể tạo ra một lỗi bằng cách cung cấp một cách rõ ràng loại:
Main.<B>fArray(new B[1], new Generic<A>(new A())); // <-- Not applicable
Tôi không thực sự tìm thấy bất cứ điều gì thông qua nghiên cứu của riêng tôi, trừ this article từ năm 2005 (trước khi generics) , nói về sự nguy hiểm của hiệp phương sai mảng.
Phương sai hiệp phương mảng dường như gợi ý về một lời giải thích, nhưng tôi không thể nghĩ ra.
jdk hiện tại là 1.8.0.0_91
Tôi tự hỏi, nếu bạn có thể gặp phải sự cố xóa kiểu Java trong Generics? Chỉ là một suy nghĩ, tôi không có thời gian để nhìn vào máy ATM gần hơn. – Ukko