1. 程式人生 > >Effective Java 第三版——27. 消除非檢查警告

Effective Java 第三版——27. 消除非檢查警告

ngs 以及 強制 學習 錯誤 scope hashset load ash

Tips
《Effective Java, Third Edition》一書英文版已經出版,這本書的第二版想必很多人都讀過,號稱Java四大名著之一,不過第二版2009年出版,到現在已經將近8年的時間,但隨著Java 6,7,8,甚至9的發布,Java語言發生了深刻的變化。
在這裏第一時間翻譯成中文版。供大家學習分享之用。

技術分享圖片

27. 消除非檢查警告

使用泛型編程時,會看到許多編譯器警告:未經檢查的強制轉換警告,未經檢查的方法調用警告,未經檢查的參數化可變長度類型警告以及未經檢查的轉換警告。 你使用泛型獲得的經驗越多,獲得的警告越少,但不要期望新編寫的代碼能夠幹凈地編譯。

許多未經檢查的警告很容易消除。 例如,假設你不小心寫了以下聲明:

Set<Lark> exaltation = new HashSet();

編譯器會提醒你你做錯了什麽:

Venery.java:4: warning: [unchecked] unchecked conversion
        Set<Lark> exaltation = new HashSet();
                               ^
  required: Set<Lark>
  found:    HashSet

然後可以進行指示修正,讓警告消失。 請註意,實際上並不需要指定類型參數,只是為了表明它與Java 7中引入的鉆石運算符("<>")一同出現。然後編譯器會推斷出正確的實際類型參數(在本例中為Lark):

Set<Lark> exaltation = new HashSet<>();

但一些警告更難以消除。 本章充滿了這種警告的例子。 當你收到需要進一步思考的警告時,堅持不懈! 盡可能地消除每一個未經檢查的警告。 如果你消除所有的警告,你可以放心,你的代碼是類型安全的,這是一件非常好的事情。 這意味著在運行時你將不會得到一個ClassCastException異常,並且增加了你的程序將按照你的意圖行事的信心。

如果你不能消除警告,但你可以證明引發警告的代碼是類型安全的,那麽(並且只能這樣)用@SuppressWarnings(“unchecked”)註解來抑制警告。 如果你在沒有首先證明代碼是類型安全的情況下壓制警告,那麽你給自己一個錯誤的安全感。 代碼可能會在不發出任何警告的情況下進行編譯,但是它仍然可以在運行時拋出ClassCastException異常。 但是,如果你忽略了你認為是安全的未經檢查的警告(而不是抑制它們),那麽當一個新的警告出現時,你將不會註意到這是一個真正的問題。 新出現的警告就會淹沒在所有的錯誤警告當中。

SuppressWarnings註解可用於任何聲明,從單個局部變量聲明到整個類。 始終在盡可能最小的範圍內使用SuppressWarnings註解。 通常這是一個變量聲明或一個非常短的方法或構造方法。 切勿在整個類上使用SuppressWarnings註解。 這樣做可能會掩蓋重要的警告。

如果你發現自己在長度超過一行的方法或構造方法上使用SuppressWarnings註解,則可以將其移到局部變量聲明上。 你可能需要聲明一個新的局部變量,但這是值得的。 例如,考慮這個來自ArrayList的toArray方法:

public <T> T[] toArray(T[] a) {
    if (a.length < size)
       return (T[]) Arrays.copyOf(elements, size, a.getClass());
    System.arraycopy(elements, 0, a, 0, size);
    if (a.length > size)
       a[size] = null;
    return a;
}

如果編譯ArrayList類,則該方法會生成此警告:
ArrayList.java:305: warning: [unchecked] unchecked cast
       return (T[]) Arrays.copyOf(elements, size, a.getClass());
                                 ^
  required: T[]
  found:    Object[]

在返回語句中設置SuppressWarnings註解是非法的,因為它不是一個聲明[JLS,9.7]。 你可能會試圖把註釋放在整個方法上,但是不要這要做。 相反,聲明一個局部變量來保存返回值並標註它的聲明,如下所示:

// Adding local variable to reduce scope of @SuppressWarnings
public <T> T[] toArray(T[] a) {
    if (a.length < size) {
        // This cast is correct because the array we're creating
        // is of the same type as the one passed in, which is T[].
        @SuppressWarnings("unchecked") T[] result =
            (T[]) Arrays.copyOf(elements, size, a.getClass());
        return result;
    }
    System.arraycopy(elements, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;
}

所產生的方法幹凈地編譯,並最小化未經檢查的警告被抑制的範圍。

每當使用@SuppressWarnings(“unchecked”)註解時,請添加註釋,說明為什麽是安全的。 這將有助於他人理解代碼,更重要的是,這將減少有人修改代碼的可能性,從而使計算不安全。 如果你覺得很難寫這樣的註釋,請繼續思考。 畢竟,你最終可能會發現未經檢查的操作是不安全的。

總之,未經檢查的警告是重要的。 不要忽視他們。 每個未經檢查的警告代表在運行時出現ClassCastException異常的可能性。 盡你所能消除這些警告。 如果無法消除未經檢查的警告,並且可以證明引發該警告的代碼是安全類型的,則可以在盡可能小的範圍內使用 @SuppressWarnings(“unchecked”)註解來禁止警告。 記錄你決定在註釋中抑制此警告的理由。

Effective Java 第三版——27. 消除非檢查警告