繁体   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