2017-10-20 19 views
5

Hôm nay, tôi phát hiện ra rằng một người có thể put một đối tượng trong một hiện tại Map ngay cả khi đối tượng không thể được truyền sang đúng loại.Đặt đối tượng có loại không nhất quán trong Bản đồ được khởi tạo - được mong đợi và hợp pháp?

Đầu tiên, hãy để tôi bắt đầu với một ví dụ đơn giản:

Map<Integer, String> myMap = new HashMap<>(); //plain old hashmap 
myMap.put(9,"star"); //no problem 

myMap.put(10, 1.2); //Incompatible type, the compiler yells 
Map<Integer, Double> aMap = (Map<Integer, Double>) myMap; //Cannot cast, the compiler yells 

Cho đến nay, tất cả mọi thứ được mong đợi, như bạn nên không có thể đặt một đối tượng kiểu không phù hợp vào một Bản đồ đã được xây dựng. Bây giờ chúng ta hãy xem xét điều này:

public class NoRulesForMe { 

    static Object theRing; 

    public static void main(String[] args){ 

     Map<Integer, String> myMap = new HashMap<>(); 
     myMap.put(9,"star"); 

     Map<Integer, Double> myMapMorphed = castWildly(myMap); 
     myMapMorphed.put(99, 3.14); 

     System.out.println(myMapMorphed.get(9)); //"star", as we put in 
     System.out.println(myMapMorphed.get(99)); //3.14, as we put in 
    } 

    public static <T> T castWildly(Object value){ 
     theRing = value; 
     T morphed = (T) theRing; 
     return morphed; 
    } 
} 

Tôi ngạc nhiên rằng điều này không gây ra một lỗi thời gian chạy - làm thế nào để đạt được điều này Bản đồ, và là hành vi này được quy định trong JLS hay API và do đó có thể được dựa trên ?

Lý do tôi hỏi là tôi thấy một phiên bản khác (có liên quan) trong mã sản xuất, và tôi tự hỏi, ngay cả khi điều này có thể bị xáo trộn và có mùi, nó có thể được đảm bảo hoạt động tốt hay không. Mội thông tin đầu vào đều sẽ được xem xét kĩ.

+0

Bạn cần google "loại xóa". –

+0

Đây là _inherent_ trong việc thực hiện các Generics của Java. –

+0

@LouisWasserman Bạn có thể xây dựng trên * những gì * là vốn có trong Generics của Java? Tôi không ngạc nhiên khi trình biên dịch không phàn nàn. Nhưng câu hỏi lớn hơn, tôi nghĩ, là cách Java cho phép một 'Double' có thể được lưu trữ như một' String'. – flow2k

Trả lời

1

Loại mã này rất nguy hiểm !! Mặc dù nó sẽ biên dịch, bạn sẽ nhận thấy rằng trình biên dịch phàn nàn với một cảnh báo:

Lưu ý: NoRulesForMe.java sử dụng các hoạt động không được kiểm tra hoặc không an toàn.
Lưu ý: Biên dịch lại bằng -Xlint: không được chọn để biết chi tiết.

Những cảnh báo này, đặc biệt là vì bạn đang sử dụng Generics, không bao giờ bị bỏ qua cũng không bị đàn áp. Bạn hoàn toàn phải chắc chắn (logic theo mã) rằng diễn viên được an toàn và sẽ không gây ra một số vấn đề sau này. Tốt nhất là luôn viết mã theo cách mà các lỗi được phát hiện và được chọn tại thời gian biên dịch thay vì thời gian chạy. Các cảnh báo được đưa ra bởi trình biên dịch ở đây là nói với bạn rằng mọi thứ có thể đi sai.

Bạn đang đi bạn myMap như một Object với phương pháp castWildly, và khi bạn đang casting bạn đang đúc từ Object đến một Map.

Trình biên dịch có thể phỏng đoán rằng T trong mã của bạn có mục tiêu loại là Map<String, Double> và do đó có thể phỏng đoán điều này. Tuy nhiên, khi truyền, nó không có thông tin về những gì (phụ) gõ Object value (hoặc Object theRing) là. Vì vậy, nó không có cách nào để kiểm tra xem dàn diễn viên có an toàn không (đặc biệt là loại an toàn).

Sự cố với mã này xuất hiện khi bạn truy xuất các giá trị ngoài bản đồ của mình. Đoạn mã sau có thêm một dòng, và mã biên dịch (với cùng cảnh báo như trên). Điều này là do khi truy xuất giá trị dưới dạng Double trên bản đồ được khai báo là Map<String, Double> hoàn toàn hợp lệ khi trình biên dịch kiểm tra loại ... lúc chạy, mã của bạn sẽ bị lỗi (lỗi thời gian chạy được hiển thị bên dưới). Đây là cách rất nguy hiểm để viết mã, đặc biệt là trong mã sản xuất. Bạn muốn có trình biên dịch của bạn cung cấp cho bạn lỗi hơn triển khai mã sản xuất biên dịch và có sản phẩm của bạn sụp đổ khi sống.

public class NoRulesForMe { 

    static Object theRing; 

    public static void main(String[] args){ 

     Map<Integer, String> myMap = new HashMap<>(); 
     myMap.put(9,"star"); 

     Map<Integer, Double> myMapMorphed = castWildly(myMap); 
     myMapMorphed.put(99, 3.14); 

     System.out.println(myMapMorphed.get(9)); //"star", as we put in 
     System.out.println(myMapMorphed.get(99)); //3.14, as we put in 

     // added to show why this style of coding causes problems 
     Double testValue1 = myMapMorphed.get(9); 
    } 

    public static <T> T castWildly(Object value){ 
     theRing = value; 
     T morphed = (T) theRing; 
     return morphed; 
    } 
} 

thời gian chạy lỗi khi chạy đoạn code trên:

sao
3.14
Exception in thread "main" java.lang.ClassCastException: java.lang.String không thể được đúc để java.lang.Double tại NoRulesForMe.main (NoRulesForMe.java:19)

Để biết thêm thông tin , đọc Java hiệu quả, bởi Joshua Bloch; Mục 24: Loại bỏ các cảnh báo không được kiểm soát. (Mục này nằm dưới tiêu đề Generics).

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