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<>();
一些警告會更加難於清除。這個章節充滿了這樣警告的例子。當你遇見需要更多思考的警告時,你需要堅持不懈!盡你所能地消除每個非受檢警告
如果你不能消除警告,但是你可以證明:引起警告的程式碼是型別安全的,那時(也只有直到那時)用@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”)註解在儘可能窄的範圍內抑制這個警告。為在註釋中抑制警告這個決定,標明基本的闡述。