2014-09-30 16 views
9

xem xét mã:Một Generics nhầm lẫn: lừa dối các trình biên dịch

public class GenericsConfusion { 
    public static <T> Class<T> get(Class<T> clazz) { 
     Map<Class, Class> map = new HashMap<Class, Class>(); 
     map.put(Integer.class, String.class); 
     return map.get(clazz); 
    } 

    public static void main(String[] args) { 
     Class<Integer> clazz = get(Integer.class); 
     System.out.println(clazz); 
    } 
} 

Nó biên dịch và chạy một cách hoàn hảo. Ý tưởng là để trở về trong phương thức get lớp có cùng tham số kiểu như lớp đầu vào. Nhưng nó bị hỏng do sự hiện diện của bản đồ. Có, tôi biết rằng trong thời gian chạy, thông tin tham số loại sẽ bị xóa, do đó, không có thông số loại, mã này hoàn toàn hợp lệ. Ngoài ra tôi biết tôi có thể sửa chữa nó bằng cách xác định Map<Class<T>, Class<T>> Nhưng thực tế là trong chữ ký phương pháp tôi có các thông số kiểu, và họ không giúp tôi trong thời gian biên dịch.

Đây có phải là lạm dụng một số khái niệm không?

Hoặc đó là một bất lợi của Generics Java?

Hoặc nó hoàn toàn ổn và tôi hiểu sai ý tưởng về các thông số loại?

+0

Đó là dấu hiệu của sự yếu kém trong hệ thống kiểu Java, xuất hiện khi các tham số kiểu được thêm vào ngôn ngữ, nhưng không cấm các kiểu thô cho khả năng tương thích ngược. – Ingo

+3

javac * thực hiện * cảnh báo các hoạt động không được kiểm tra hoặc không an toàn trong mã này. –

+1

Vâng, tôi sẽ gọi điều này * bỏ qua *, không * lừa dối *, trình biên dịch. Trình biên dịch biết những gì đang xảy ra. –

Trả lời

8

Một liệu loại, chẳng hạn như Class hoặc Map (như trái ngược với Class<...> hoặc Map<..., ...>), có thể làm hỏng các loại kiểm tra của Generics. Bạn thậm chí có thể viết một cái gì đó như thế này:

final Class<Integer> whoops = (Class) String.class; 

Đây là một điểm yếu đáng tiếc trong hệ thống kiểu. Nó ban đầu được bao gồm trong Java 5 (được giới thiệu generics) cho khả năng tương thích với mã được viết theo các phiên bản trước.

Đối với hầu hết các phần, bạn có thể tránh điểm yếu này bằng cách tránh sử dụng các loại thô. Trình biên dịch của bạn nên cảnh báo bạn về chúng.

Thật không may, có những hoàn cảnh khác nhau, nơi các loại nguyên liệu cơ bản là không thể tránh khỏi (do gõ đặc biệt của .getClass(); do thực tế rằng chúng tôi chỉ có thể viết (ví dụ) Map.class và không Map<String, String>.class (hoặc Map.<String, String>class); do tẩy xoá và phản chiếu, vv); nhưng hạnh phúc, như bạn đã lưu ý, hoàn cảnh của bạn dường như không phải là một trong số này.

0

Tôi đoán bạn sẽ trả lại thứ gì đó từ Bản đồ?

Hiện nay, bạn có lý thuyết có thể lưu trữ một cái gì đó khác hơn Class<T> trong Map như Class<Y>, mặc dù không phải từ mã này nhưng bị rò rỉ tài liệu tham khảo vv Để trở lại một cái gì đó từ bản đồ như một Class<T> bạn phải buộc bản đồ để lưu trữ chỉ có Class<T> và không có gì khác.

Hiện tại, nó có thể lưu trữ Class<T>Class<Y> cùng một lúc tôi tin.