2012-02-01 31 views
16

此代碼不會編譯,因爲存在對靜態字段的非法引用。爲什麼這個枚舉代碼是一個非法引用靜態字段?

public enum Foo { 

    A, 
    B; 

    private Foo[] foos = new Foo[] { Foo.A }; 

} 

您不應該能夠從非靜態字段初始值設定項訪問靜態字段嗎?例如:

public class Foo { 

    static int A; 

    private int[] foos = new int[] { Foo.A }; 

} 

這個編譯好。

注意,使第一個示例中的foos靜態編譯。

回答

16

退房Java語言規範,第三版,在http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.9

8.9節這是一個編譯時錯誤引用一個枚舉類型 不是一個編譯時間常數的靜態字段(§ 15.28), 實例初始化塊,或實例變量初始化程序 該類型的表達式。 構造函數,實例初始化塊或實例變量 的枚舉常量e的初始值設定項表達式引用自身,或與 相同類型的枚舉常量聲明爲e的右側,這是編譯時錯誤。

討論

如果沒有這個規則,顯然是合理的代碼將在運行時 失敗的原因在枚舉類型固有的初始化圓。 (A 圓在任何類存在與「自鍵入了」靜態字段。) 這裏是排序代碼的一個例子,將失敗:

enum Color { 
     RED, GREEN, BLUE; 
     static final Map<String,Color> colorMap = 
     new HashMap<String,Color>(); 
     Color() { 
      colorMap.put(toString(), this); 
     } 
    } 

該枚舉類型的靜態初始化會拋出一個 NullPointerException,因爲靜態變量colorMap是 當枚舉常量的構造函數運行時未初始化。上述限制 可確保此類代碼不會編譯。

7

寫出一個大致相當於,更簡單的方式,更接近字節碼,我們可以看到:

public final class Foo { 
    public static final Foo A = new Foo(); 
    public static final Foo B = new Foo(); 

    private Foo[] foos; 

    private Foo() { 
     this.foos = new Foo[] { Foo.A }; 
    } 
} 

你可以看到,初始化A我們調用構造函數,其內容爲A。顯然,雖然仍在建設者A將不會被初始化。

(事實證明,這個簡單的代碼不會編譯,它只是不會做你所期望的東西。)

你可能想Foo.values()代替foos實例變量。

相關問題