簡體   English   中英

什么是未經檢查的演員表,我該如何檢查?

[英]What is unchecked cast and how do I check it?

我想我明白了 unchecked cast 的意思(從一個不同類型的轉換到另一個類型),但是“檢查”轉換是什么意思? 如何檢查演員表,以便在 Eclipse 中避免此警告?

詳細說明彼得所寫的內容:

從非泛型類型到泛型類型的轉換在運行時可能工作得很好,因為泛型參數在編譯期間被刪除,所以我們留下了一個合法的轉換。 但是,由於對類型參數的假設不正確,代碼稍后可能會失敗並出現意外的ClassCastException 例如:

    List l1 = new ArrayList();
    l1.add(33);
    List<String> l2 = (List<String>) l1;
    String s = l2.get(0);

第 3 行的 unchecked 警告表明編譯器無法保證類型安全,因為稍后可能會發生意外的 ClassCastException。 事實上,這發生在第 4 行,它執行了一個隱式轉換。

未經檢查的強制轉換意味着您(隱式或顯式)從泛型類型轉換為非限定類型,或者相反。 例如這條線

Set<String> set = new HashSet();

會產生這樣的警告。

通常此類警告是有充分理由的,因此您應該嘗試改進代碼而不是抑制警告。 引自 Effective Java,第 2 版:

盡可能消除所有未經檢查的警告。 如果您消除了所有警告,您就可以確信您的代碼是類型安全的,這是一件非常好的事情。 這意味着您不會在運行時收到ClassCastException ,並增加您對程序按預期運行的信心。

如果您無法消除警告,並且您可以證明引發警告的代碼是類型安全的,那么(並且只有這樣)使用@SuppressWarnings("unchecked")注釋抑制警告。 如果你在沒有首先證明代碼是類型安全的情況下抑制警告,你只會給自己一種錯誤的安全感。 代碼可以編譯而不發出任何警告,但它仍然可以在運行時拋出ClassCastException 但是,如果您忽略您知道是安全的未經檢查的警告(而不是抑制它們),您將不會注意到何時出現代表真正問題的新警告。 新警告將在您沒有靜音的所有誤報中消失。

當然,消除警告並不總是像上面的代碼那樣容易。 如果沒有看到您的代碼,就無法知道如何使其安全。

與已檢查的強制轉換相反,未檢查的強制轉換不會在運行時檢查類型安全。

這是一個基於第三版的Consider typesafe heterogenous containers部分的示例。 Joshua Bloch 的“Effective Java”,但容器類被故意破壞 - 它存儲並返回錯誤的類型:

public class Test {

    private static class BrokenGenericContainer{
        private final Map<Class<?>, Object> map= new HashMap<>();

        public <T> void store(Class<T> key, T value){
            map.put(key, "broken!"); // should've been [value] here instead of "broken!"
        }

        public <T> T retrieve(Class<T> key){
//          return key.cast(map.get(key)); // a checked cast 
            return (T)map.get(key);        // an unchecked cast
        }

    }

    public static void main(String[] args) {
        BrokenGenericContainer c= new BrokenGenericContainer();
        c.store(Integer.class, 42);
        List<Integer> ints = new ArrayList<>();
        ints.add(c.retrieve(Integer.class));
        Integer i = ints.get(0);
    }

}


如果retrieve()使用未經檢查的強制轉換- (T)map.get(key) - 運行此程序將導致在Integer i = ints.get(0)行發生ClassCastException retrieve()方法將完成,因為在運行時未檢查實際類型:

Exception in thread "main" 
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
    at Test.main(Test.java:27)


但是如果retrieve()使用一個checked cast - key.cast(map.get(key)) - 運行這個程序將導致在key.cast(map.get(key))行發生ClassCastException ,因為checked cast發現類型錯誤並拋出異常。 retrieve()方法不會完成:

Exception in thread "main" java.lang.ClassCastException: 
                                          Cannot cast java.lang.String to java.lang.Integer
    at java.lang.Class.cast(Class.java:3369)
    at Test$BrokenGenericContainer.retrieve(Test.java:16)
    at Test.main(Test.java:26)

看起來差別不大,但在未經檢查的強制轉換的情況下,一個String成功地進入了一個List<Integer> 在現實世界的應用中,其后果可能是……好吧,嚴重。 如果使用已檢查的強制轉換,則會盡早發現類型不匹配。


為了避免未經檢查的強制轉換警告,可以使用@SuppressWarnings("unchecked") ,如果程序員真的確定該方法實際上是安全的。 更好的選擇是在可能的情況下使用泛型和檢查強制轉換。

正如約書亞·布洛赫所說,

...未經檢查的警告很重要。 不要忽視它們。


為完整起見,答案涉及 Eclipse 細節。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM