为什么我无法从 Java 中的专用枚举值访问静态最终成员

2022-09-03 15:13:02

我想知道为什么,虽然在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

现在我明白了,等实际上通常都被视为静态的最终实例类型,因此没有这些字段,但是信息理论上应该在编译时可用,这可以很容易地验证运行一点测试。VALUE1VALUE2Test

     // 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

很明显,我们实际上在运行时为 和 提供了适当的专用子类。但是,看起来我们丢失有关具体类型信息的原因,以及编译器为基枚举类生成静态枚举值的方式,如:VALUE1VALUE2VALUE1VALUE2TestVALUE3Test

但是,在我看来,如果编译器只是简单地保持这些类型

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

所有周围的代码仍然有效。此外,我们还可以执行我最初尝试并通过 访问 ,现在这显然是类型的实例,而不仅仅是,虽然通常应该避免,但在这种情况下,通过实例访问该静态成员似乎完全没问题。CONST_RELATED_TO_VALUE1Test.VALUE1Test$1Test

现在,正如许多人正确指出的那样,在左侧使用匿名类不是有效的Java代码,并且可能也适用于编译器,如果没有一些重大的规范更改,则不允许这样做。但是,这可以通过使用命名的内部类轻松解决,因此我们将具有

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

这甚至为调试提供了额外的好处,即内部子类名称清楚地映射到相应的枚举值。

现在我明白了必须有一些小的改变,但是从匿名类到命名类,而不是丢弃类型似乎是一个很小的变化,这看起来是一个非常好的功能,没有一个简单的方法来模拟它使用覆盖的成员或其他东西。

所以我想知道为什么除了时间之外,这没有像这样实现?我在这里是否遗漏了一些关键的东西来阻止编译器这样做(无论是关于实现复杂性还是类型系统不可能性),它只是没有实现以保持它更简单,因为没有时间或沿着这些路线的东西?

(我主要是从编译器/类型系统的角度寻找为什么决定像这样实现它的原因,而不是为了实际的替代方案,因为肯定有几个,尽管它似乎仍然是一个不错的模式)


答案 1

静态成员(如 )是相应枚举值的匿名类的成员,但不是枚举类本身的成员。与其他匿名类一样,此处的对象被声明为类型,即使它是 的匿名子类的实例。CONST_RELATED_TO_VALUE1VALUE1TestTest

因此,您不能通过 访问,因为它是类型的引用,并且是匿名子类的成员,但不是 。 只能由匿名类的其他成员访问。CONST_RELATED_TO_VALUE1VALUE1.CONST_RELATED_TO_VALUE1VALUE1TestCONST_RELATED_TO_VALUE1TestCONST_RELATED_TO_VALUE1

如果要访问 在 的匿名类中定义的值,则需要有一个枚举类型的方法(例如),该方法在 enum 对象的匿名类中重写,并且该方法返回或以某种方式提供要公开的值(通过 )。VALUE1m()VALUE1.m()


答案 2

枚举常量是属于其声明枚举类型的特殊变量。换句话说,引用表达式的类型为 。该类型不定义变量名称,因此您无法访问变量名称。Test.VALUE1TestTESTCONST_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


枚举常量的可选主体定义扩展枚举类型的新匿名类。