[英]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
訪問常量不起作用。
現在我明白了, VALUE1
, VALUE2
等實際上通常都被視為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
很明顯,我們在運行時為VALUE1
和VALUE2
提供了適當的專用子類。 但是看起來我們丟失有關VALUE1
和VALUE2
的具體類型信息的原因是編譯器為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
,因為VALUE1
是Test
類型的引用, 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);
我們將得到編譯錯誤,因為f
是Foo
類型,沒有聲明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.