簡體   English   中英

為什么我不能從Java中的專用枚舉值訪問靜態最終成員

[英]Why can't I access static final members from a dedicated enum value in Java

我想知道為什么,盡管在Java中執行以下操作是完全有效的

public enum Test {
   VALUE1() {
      public static final String CONST_RELATED_TO_VALUE1 = "constant";
      public static final String OTHER_CONST_RELATED_TO_VALUE1 = "constant";
   },
   VALUE2() {
      public static final String CONST_RELATED_TO_VALUE2 = "constant";
   },
   VALUE3;
}

正如人們期望的那樣使用Test.VALUE1.CONST_RELATED_TO_VALUE1訪問常量不起作用。

現在我明白了, VALUE1VALUE2等實際上通常都被視為Test類型的靜態最終實例,因此沒有那些字段,但理論上信息應該在編譯時可用,這可以很容易地通過運行一點測試來驗證

     // print types and static members
     for (Object o: Test.values()) {
        System.out.println(o.toString() + ": " + o.getClass());
        for (Field field : o.getClass().getDeclaredFields()) {
            if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
                System.out.println("\t" + field);
            }
        }             
     }

這導致以下輸出

VALUE1: class Test$1                                                                                                                                                                               
        public static final java.lang.String Test$1.CONST_RELATED_TO_VALUE1                                                                                                                
        public static final java.lang.String Test$1.OTHER_CONST_RELATED_TO_VALUE1                                                                                                          
VALUE2: class Test$2                                                                                                                                                                               
        public static final java.lang.String Test$2.CONST_RELATED_TO_VALUE2                                                                                                                
VALUE3: class Test                                                                                                                                                                                 
        public static final Test Test.VALUE1                                                                                                                                    
        public static final Test Test.VALUE2                                                                                                                                    
        public static final Test Test.VALUE3                                                                                                                                    
        private static final Test[] Test.$VALUES

很明顯,我們在運行時為VALUE1VALUE2提供了適當的專用子類。 但是看起來我們丟失有關VALUE1VALUE2的具體類型信息的原因是編譯器為VALUE3使用的基本枚舉類Test生成靜態枚舉值的方式:所有成員都是Test類型,具體類型被丟棄。

但是,在我看來,如果編譯器只是保留這些類型

        public static final Test$1 Test.VALUE1                                                                                                                                    
        public static final Test$2 Test.VALUE2                                                                                                                                    
        public static final Test Test.VALUE3                                                                                                                                    

所有周圍的代碼仍然有效。 此外,我們也可以做我最初嘗試的並通過Test.VALUE1訪問CONST_RELATED_TO_VALUE1 ,現在它顯然是Test$1類型的實例,而不僅僅是Test ,雖然通常應該避免,但在這種情況下似乎完全可以訪問靜態成員通過實例。

現在正如許多人正確指出的那樣,使用左側的匿名類不是有效的Java代碼,並且可能也不允許在沒有一些主要規范更改的情況下編譯器。 但是,這可以通過使用命名的內部類來輕松解決,所以我們可以

        public static final Test.Value1 Test.VALUE1                                                                                                                                    
        public static final Test.Value2 Test.VALUE2                                                                                                                                    
        public static final Test Test.VALUE3                                                                                                                                    

這甚至為調試提供了額外的好處,即內部子類名稱清楚地映射到相應的枚舉值。

現在我明白了必須進行一些細微的改動,但是從匿名到命名的類而不是丟棄這些類似乎是一個很小的改變,這看起來很不錯,沒有一個簡單的方法來使用重寫來模擬它成員或什么的。

所以我想知道為什么除了時間之外沒有像這樣實現? 我是否遺漏了一些關鍵的東西,這會阻止編譯器這樣做(關於實現復雜性或類型系統不可能)是否只是為了保持簡單而沒有實現,因為沒有時間或某些東西沿着這些線?

(我主要是尋找為什么決定從編譯器/類型系統的角度來實現它的原因,而不是實際的替代方案,因為肯定有一對,盡管它似乎仍然是一個很好的模式)

靜態成員(如CONST_RELATED_TO_VALUE1是相應枚舉值的匿名類的成員,但不是枚舉類本身的成員。 與其他匿名類一樣,此處的對象VALUE1被聲明為Test類型,即使它是Test的匿名子類的實例。

因此,您無法通過VALUE1.CONST_RELATED_TO_VALUE1訪問CONST_RELATED_TO_VALUE1 ,因為VALUE1Test類型的引用, CONST_RELATED_TO_VALUE1是匿名子類的成員但不是Test CONST_RELATED_TO_VALUE1只能由匿名類的其他成員訪問。

如果要訪問VALUE1的匿名類中定義的值,則需要在枚舉對象的匿名類中使用枚舉類型的方法(例如, m() ),並以某種方式返回或提供您要公開的值(通過VALUE1.m() )。

枚舉常量是特殊變量,它們是聲明枚舉類型的類型。 換句話說,引用表達式Test.VALUE1的類型為Test TEST類型沒有定義變量名CONST_RELATED_TO_VALUE1 ,因此您無法訪問它。

這與做的類似

class Parent {
}

class Child extends Parent {
    public Object field = new Object();
}
...
Parent ref = new Child(); 
System.out.println(ref.field); // compilation error

除非在您的情況下,您嘗試通過引用表達式訪問static字段。


枚舉常量的可選主體定義了擴展枚舉類型的新匿名類。

VALUE1() {
  public static final String CONST_RELATED_TO_VALUE1 = "constant";
  public static final String OTHER_CONST_RELATED_TO_VALUE1 = "constant";
}

是一個擴展Test枚舉的匿名類。 在這樣的類的情況下,我們可以訪問其成員(沒有反射的幫助)只有當我們在創建它之后直接執行它時:

class Foo{
    public static void main(String[] args) {
        System.out.println(new Foo(){
            public String x = "foo";
        }.x);
    }
}

但如果我們寫下這樣的話:

Foo f = new Foo(){
    public String x = "foo";
};
System.out.println(f.x);

我們將得到編譯錯誤,因為fFoo類型,沒有聲明x成員。
這就是你的枚舉問題。 你在這做了什么:

VALUE1() {
  public static final String CONST_RELATED_TO_VALUE1 = "constant";
  public static final String OTHER_CONST_RELATED_TO_VALUE1 = "constant";
}

實際上是:

public static final Test VALUE1 = new Test(){
//                  ^^^^^^^^^^^
  public static final String CONST_RELATED_TO_VALUE1 = "constant";
  public static final String OTHER_CONST_RELATED_TO_VALUE1 = "constant";
}

因此,您看到VALUE1 (和其他枚舉常量)的類型為Test ,而不是Test$1 (編譯器給出的匿名類的名稱)。

為什么選擇Test類型而不是Test$1 好吧,這可能是因為變量不能用匿名類型聲明 (我們不能使用Test$1 foo變量),並且所有枚舉類型實際上都被編譯成擴展Enum類的簡單類,因此相同的規則必須適用於其字段(常量) )。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM