2017-10-05 68 views
1

考慮以下枚舉建設:使用私有靜態成員構建枚舉在Java

public enum MyEnum { 
    ENUM_A(TYPE1), ENUM_B(TYPE2), ENUM_C(TYPE1), ENUM_D(TYPE1); 
    private static final String TYPE1 = "I am type 1"; 
    private static final String TYPE2 = "This is type 2"; 
    private final String type; 
    private MyEnum(String type) { 
     this.type = type; 
    } 
    public String getType() { 
     return type; 
    } 
} 

上面的代碼不能編譯,因爲編譯器在抱怨illegal forward reference。我有點理解爲什麼常量被定義爲之後的枚舉。

但是,不能移動枚舉定義上面/之前的常量,因爲枚舉定義必須是枚舉類中的第一個東西。

現在,如果代碼被更改爲:

public enum MyEnum { 
    ENUM_A(MyEnum.TYPE1), ENUM_B(MyEnum.TYPE2), ENUM_C(MyEnum.TYPE1), ENUM_D(MyEnum.TYPE1); 
    private static final String TYPE1 = "I am type 1"; 
    private static final String TYPE2 = "This is type 2"; 
    private final String type; 
    private MyEnum(String type) { 
     this.type = type; 
    } 
    public String getType() { 
     return type; 
    } 
} 

然後,它似乎工作。

我的問題是:更改後的代碼是否帶有任何潛在的問題,即使它編譯?爲什麼更改可以解決編譯器檢查問題?這是否被認爲是一種標準方式或繞過未來可能修復的編譯器檢查的方式?

回答

1

或者,您可以將enum放在常規類中,並將常量聲明爲類枚舉的枚舉。

public class ConstantTypes { 

    private static String type1 = "type 1" 
    private static String type2 = "type 2" 

    public enum Types { 
     A(type1) {}, 
     B(type1) {}, 
     C(type2) {}, 
     D(type2) {}; 
     private final String type; 
     private Types(String typ) { 
      type = typ; 
     } 
     public String getType() { return type; } 
    } 
} 

編輯:要直接回答你的問題,你是不是正好本身在你的第二個代碼示例「周圍的編譯器檢查工作」。在那裏你以靜態的方式訪問靜態值,而在第一個例子中,你不是。我知道你的第二個例子沒有潛在的問題(除了可讀性),代碼編譯,你很好。

+0

謝謝你的選擇。我沒有看到第一個和第二個不同的含義(語法除外)。但是你回答了我的問題,爲什麼它的工作。所以我選擇你作爲答案。 – user1589188

+0

@ user1589188編譯器運行幾次,並且可以以任何順序初始化靜態變量值,除非您特別插入代碼的「靜態初始化塊」來控制值的初始化方式。所以你的例子之間的區別在於,第二,當你以「靜態方式」訪問變量時,它們保證被初始化。在第一個例子中,「保證」並不存在,當你引用它們時,它們可能會或可能不會被初始化(並且每次編譯時初始化順序都會改變) – geneSummons

2

這是一個完全可以接受的解決方案,除了額外的混亂之外,我想不出任何缺點。

如果你想刪除混亂,你可以使用這個事實,即interface可以保持不變的值。這可能會打破一些程序員最不感到驚訝的原則,但我會在我的代碼中接受它。

private interface MyConstants { 
    String TYPE1 = "I am type 1"; 
    String TYPE2 = "This is type 2"; 
} 

public enum MyEnum implements MyConstants{ 
    ENUM_A(TYPE1), ENUM_B(TYPE2), ENUM_C(TYPE1), ENUM_D(TYPE1); 
    private final String type; 
    private MyEnum(String type) { 
     this.type = type; 
    } 
    public String getType() { 
     return type; 
    } 
} 
+0

啊,是的,謝謝你的替代方案。我其實希望能回答我的問題,而不是想到的建議。但你應該得到讚揚。 – user1589188

1

有一些問題,是的。通過規避不使用尚未聲明的字段的限制(通過引用聲明這些靜態字段的類(它可能不是當前類並因此繞過了編譯器的驗證,指出了這些字段已被聲明))的限制,您在這些字段存在之前使用這些字段。

它在您的示例中正常工作,因爲您的字段恰好是常量:它們是聲明爲最終的字符串文字。因爲編譯器在編譯時知道它們的值並將MyEnum.TYPE1替換爲MyEnum.TYPE1的已知常量值。

如果您將字段設置爲非常量,它將停止工作,但它仍然會編譯並且僅在運行時提供不想要的結果。例如,嘗試:

private static final String TYPE1 = new String("I am type 1"); 

不再是一個常數= >不再工作。

+0

謝謝。我嘗試了你所說的並猜測是什麼,編譯器實際上可以抓住這一點,並像第一個例子中那樣抱怨'非法前向引用'。所以我們仍然保守。 – user1589188

+0

@ user1589188啊,好抓。我通常使用的編譯器不會給出錯誤,但官方的Oracle編譯器會這樣做。檢查語言規範我找不出如何構成非法前向引用。值得注意的是,如果語言不是一個常量,那麼語言就不應該接受一個前向引用,否則就拒絕它。這是否是一個常數應該是無關緊要的。我必須進一步檢查。也許請記住,所有編譯器都不會將其視爲錯誤,但編譯器通常通過Java一致性套件進行測試。 – kumesana