英文原文 http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf
譯文:http://www.360doc.com/showWeb/0/0/361230.aspx
靜態成員中的封閉類型參數
編譯器完全禁止在靜態方法和靜態內部類中引用封閉類型參數。所以,舉例來說,以下代碼在 Tiger 中就是非法的:
清單 1. 在靜態上下文中非法引用封閉類型參數
class C<T> {
static void m() {
T t;
}
static class D {
C<T> t;
}
}
當編譯這一代碼時,會生成兩個錯誤:
在靜態方法 m 中非法引用 T 的錯誤
在靜態類 D 中非法引用 T 的錯誤
當定義靜態字段時,情況變得更加復雜。在 JSR-14 和 Tiger 中,在泛型類的所有實例中共享該類中的靜態字段。現在,在 JSR-14 編譯器 1.0 和 1.2 中,如果您在靜態字段聲明中引用類型參數,編譯器不會報錯,但它本應該這么做。字段被共享這一事實很容易在運行時導致奇怪的錯誤,如在不包含數據類型轉換的代碼中出現 ClassCastException 。
例如,以下程序將在這兩個版本的 JSR-14 下通過編譯而沒有任何警告:
清單 2. 在靜態字段中對封閉類型參數的有問題的引用
class C<T> {
static T member;
C(T t) { member = t; }
T getMember() { return member; }
public static void main(String[] args) {
C<String> c = new C<String>("test");
System.out.println(c.getMember().toString());
new C<Integer>(new Integer(1));
System.out.println(c.getMember().toString());
}
}
請注意,每次分配類 C 的實例時,都要重新設置靜態字段 member 。而且,它被設置成的對象類型取決於 C 的實例的類型!在所提供的 main 方法中,第一個實例 c 是 C<String> 類型。而第二個是 C<Integer> 類型。每當從 c 訪問 member 這一共享靜態字段時,總是假定 member 的類型是 String 。但是,在分配了類型為 C<Integer> 的第二個實例之后, member 的類型是 Integer 。
運行 C 的 main 方法的結果可能會讓您吃驚 ― 它將發出一個 ClassCastException !源代碼根本沒有包含任何數據類型轉換,怎么會這樣呢?事實證明編譯器確實在編譯階段將數據類型轉換插入到代碼中,這樣做是為了解決類型擦除會降低某些表 達式的類型的精度這一事實。這些數據類型轉換 被期望能夠成功,但在本例中卻沒有成功。
應該認為 JSR-14 1.0 和 1.2 的這一特殊“功能”是個錯誤。它破壞了類型系統的健全性,或者可以說,它破壞了類型系統應該和程序員達成的“基本契約”。象對靜態方法和類所做的那樣,只要防止程序員在靜態字段中引用泛型類型,情況就會好很多。
請注意允許這種有潛在“爆炸性”的代碼存在所帶來的問題並不是程序員 有意在自己的代碼中覆蓋類型系統。問題是程序員可能會無意中編寫這樣的代碼(比如,由於“復制和粘貼”操作,錯誤地在字段聲明中包括靜態修飾符)。
類型檢查器應該能幫助程序員從這些類型的錯誤中恢復,但對於靜態字 段而言,類型系統實際上會使程序員更迷惑。當未使用數據類型轉換的代碼中顯示的唯一錯誤就是 ClassCastException 時,我們應如何診斷這樣的錯誤?對於不清楚 Tiger 中泛型類型所用的實現方案而又恰好假定類型系統合理運行的程序員而言,情況更糟。因為在這樣的情況下,類型系統不是合理地運行。
幸運的是,JSR-14 的最新版本(1.3)宣布在靜態字段中使用類型參數是不合法的。因此,我們有理由期待在 Tiger 的靜態字段中使用類型參數也是不合法的。