1. 程式人生 > >Effective Java 3rd 條目27 消除非受檢警告

Effective Java 3rd 條目27 消除非受檢警告

當你用泛型程式設計時,你將會看見許多編譯器警告:非受檢強轉警告、非受檢方法呼叫警告、非受檢引數化vararg型別警告和非受檢轉換警告。你多熟悉泛型一些,你將獲得更少警告,但是不要期待這些新程式碼乾淨利索地編譯。

許多非受檢警告容易消除。例如,假設你不慎編寫如下宣告:

Set<Lark> exaltation = new HashSet();

編譯器會輕輕地告訴你做錯了什麼:

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

然後你可以進行指示的改正,使得警告消失。記住,你實際上不需要指定型別引數,僅僅用菱形操作子(diamond operator)(<>)(在Java7引入)表示。然後編譯器會推導(infer)正確的實際型別引數(在這個情形中,Lark):

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

一些警告會更加難於清除。這個章節充滿了這樣警告的例子。當你遇見需要更多思考的警告時,你需要堅持不懈!盡你所能地消除每個非受檢警告

。如果你消除了所有警告,那麼你敢保證你的程式碼是型別安全,這是一個非常好的事情。這意味著,你不會在執行時遇見ClassCastException,而且它增加了你的信心:你的程式碼將像預期的一樣執行。

如果你不能消除警告,但是你可以證明:引起警告的程式碼是型別安全的,那時(也只有直到那時)用@SuppressWarnings(“unchecked”)註解抑制警告。如果你抑制警告,而沒有首先證明這個程式碼是型別安全的,那麼你給自己關於安全的錯誤感覺,但是它仍舊可能在執行時丟擲ClassCastException。然而,如果你忽略了你知道是安全的非受檢警告(而不是抑制它),那麼你不會知道一個新警告什麼時候突然出現,而這個警告表示一個真正問題。新警告將會消失在所有你沒有使之靜默的錯誤警報當中。

從單個本地變數宣告到整個類,SuppressWarnings註解可以使用在任何宣告。永遠在儘可能小範圍內使用SuppressWarnings註解。通常,這將會是變數宣告或者一個很短的方法或者構造子。永遠不要在整個類上使用SuppressWarnings。這樣做可能隱藏了關鍵的警告。

如果你發現自己在一個方法或者構造子上使用SuppressWarnings註解,這個方法或者構造子長於一行,那麼你可以把它移動到一個本地變數宣告。你可能不得不宣告一個新的本地變數,但是這是值得的。例如,考慮如下toArray方法,它來自於ArrayList:

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]。你可能想把註解放到整個方法上,但是不要這麼做。反而,宣告一個本地變數來保留返回值,而且註解它的宣告,就像如此:

// 為了減少@SuppressWarnings範圍而新增本地變數
public <T> T[] toArray(T[] a) {
    if (a.length < size) { 
        // 整個強轉是正確的,因為我們建立的這個佇列,是和傳入的這個是相同型別的,即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”)註解在儘可能窄的範圍內抑制這個警告。為在註釋中抑制警告這個決定,標明基本的闡述。