2015-05-02 15 views
16

Xét đoạn mã sau:Tại sao phương thức gọi với trả về chung trên một lớp chung được coi là không an toàn bởi javac?

public class Main { 
    public static class NormalClass { 
     public Class<Integer> method() { 
      return Integer.class; 
     } 
    } 

    public static class GenericClass<T> { 
     public Class<Integer> method() { 
      return Integer.class; 
     } 
    } 

    public static void main(String... args) { 
     NormalClass safeInstance = new NormalClass(); 
     Class<Integer> safeValue = safeInstance.method(); 

     GenericClass unsafeInstance = new GenericClass(); 
     Class<Integer> unsafeValue = unsafeInstance.method(); 
    } 
} 

Nếu tôi biên dịch nó với:

$ javac -Xlint:unchecked Main.java 

Nó trả về:

Main.java:16: warning: [unchecked] unchecked conversion 
     Class<Integer> unsafeValue = unsafeInstance.method(); 
                 ^
    required: Class<Integer> 
    found: Class 
1 warning 

Xin lưu ý rằng chỉ có phương pháp chung được coi là không an toàn, ngay cả khi không có loại chung nào được tham chiếu trên kiểu trả về.

Đây có phải là lỗi javac không? Hoặc có một lý do sâu sắc hơn cho điều này tôi không tính đến?

+0

Tôi không biết lý do, nhưng nó chỉ xảy ra nếu lớp chung mà phương thức được khai báo là kiểu thô. – Bubletan

+0

Tôi nhận được lỗi trên GenericClass unsafeInstance = new GenericClass(); thay thế. Điều này là do bạn đã khởi tạo một kiểu generic mà không có đối số kiểu. –

+6

Bởi vì khi bạn chọn sử dụng các loại thô, về cơ bản bạn đang nói với trình biên dịch mà bạn muốn ở chế độ cũ không an toàn. Không sử dụng các loại thô. Đọc http://stackoverflow.com/questions/2770321/what-is-a-raw-type-and-why-shouldnt-we-use-it –

Trả lời

12

Các loại thô được phép đảm bảo tính tương thích với mã được viết trước khi Generics được giới thiệu. Các kiểu thô hoạt động bằng cách đơn giản bỏ qua tất cả thông tin loại từ tất cả đối số phương thức và kiểu trả về, thậm chí nhập thông tin không liên quan đến tham số kiểu của lớp. Điều này có thể dẫn đến kết quả lạ, như bạn đã tìm thấy. Nhưng nó thậm chí còn xa lạ hơn thế này. Ví dụ, biên dịch này.

public class Main { 

    public static class GenericClass<T> { 
     public void foo(Class<Integer> clazz) { 
     } 
    } 

    public static void main(String... args) { 
     GenericClass unsafeInstance = new GenericClass(); 
     unsafeInstance.foo(String.class); 
    } 
} 
+1

... nhưng không phải nếu bạn chỉ định tham số kiểu chung tại thời điểm khởi tạo, mặc dù điều này không ảnh hưởng đến phương thức 'foo'. Ví dụ thực sự thú vị! – isnot2bad

2

Để tương thích với trình biên dịch Java 1.4 giả định rằng nếu bạn loại bỏ các đối số chung trên khai báo kiểu cá thể, thì bạn làm việc với phiên bản đặc biệt của lớp không có generics. Và nó đưa ra cảnh báo nếu bạn kết hợp mã Java không chung chung với mã Java 1.5+ chung chung. Đó là dễ dàng hơn so với cố gắng tìm ra liệu kiểu trả về chung của phương thức của bạn có thực sự độc lập với các tham số hay không. Bạn luôn có thể @SuppressWarning nếu bạn không thích nó.

3

Loại phương thức khởi tạo (§8.8), phương pháp thể hiện (§8.4, §9.4) hoặc trường không tĩnh (§8.3) M của loại thô C không được thừa kế từ các siêu lớp hoặc siêu kết nối là kiểu thô tương ứng với xóa các loại của nó trong việc kê khai chung tương ứng với C.

Java Language Specification

+0

Mặc dù tôi chấp nhận câu trả lời khác, tôi nghĩ câu trả lời này cũng xứng đáng được nhiều người ủng hộ hơn, bởi vì nó trích dẫn đặc điểm kỹ thuật, làm rõ đây là một quyết định thiết kế cụ thể, chứ không phải tác dụng phụ không mong muốn của việc thực hiện. –

+1

Ngoài ra [4.6] (https://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.6): * "Loại tẩy xoá cũng ánh xạ chữ ký của một phương thức đến một chữ ký không có kiểu tham số. [...] kiểu trả về của một phương thức cũng sẽ bị xóa nếu chữ ký của phương thức bị xóa "*. – Radiodef

0

này có thể phải làm với các instantiation của GenericClass mà là một kiểu tham số và do đó gõ luận là cần thiết trong đó. Cảnh báo biến mất nếu chúng ta làm điều gì đó như sau

GenericClass<String> unsafeInstance = new GenericClass<String>(); 
          OR 
GenericClass<?> unsafeInstance = new GenericClass(); 

Theo quan điểm của tôi, tài liệu tham khảo "unsafeInstance" đề cập đến một lớp học mà là chung chung trong tự nhiên như trái ngược với "safeInstance". Do đó trình biên dịch có thể muốn thông tin kiểu được liên kết với tham chiếu trước khi bất kỳ phương thức nào được gọi là sử dụng nó trong mã.

Các vấn đề liên quan