简体   繁体   English

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

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

I was wondering why, while it's perfectly valid to do the following 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;
}

accessing the constants as one would expect using Test.VALUE1.CONST_RELATED_TO_VALUE1 does not work. 正如人们期望的那样使用Test.VALUE1.CONST_RELATED_TO_VALUE1访问常量不起作用。

Now I understand, VALUE1 , VALUE2 etc. are actually all generally seen as static final instance of type Test and hence don't have those fields, but the information should theoretically available at compile time, which can easily be verified running a little test 现在我明白了, 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);
            }
        }             
     }

which results in the following output 这导致以下输出

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

It's clear that we actually have proper dedicated sub-classes at runtime for VALUE1 and VALUE2 . 很明显,我们在运行时为VALUE1VALUE2提供了适当的专用子类。 But it also looks like the reason we lose the concrete type information about VALUE1 and VALUE2 is the way the compiler generates the static enum values for the base enum class Test as used by VALUE3 : All members are of type Test and the concrete types are discarded. 但是看起来我们丢失有关VALUE1VALUE2的具体类型信息的原因是编译器为VALUE3使用的基本枚举类Test生成静态枚举值的方式:所有成员都是Test类型,具体类型被丢弃。

However, it seems to me that if the compiler simply kept those types like so 但是,在我看来,如果编译器只是保留这些类型

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

all surrounding code would still work. 所有周围的代码仍然有效。 In addition we could also do what I tried initially and access CONST_RELATED_TO_VALUE1 through Test.VALUE1 , which is now clearly an instance of type Test$1 and not just Test and while it should be generally avoided, it seems in this case perfectly fine to access that static member through an instance. 此外,我们也可以做我最初尝试的并通过Test.VALUE1访问CONST_RELATED_TO_VALUE1 ,现在它显然是Test$1类型的实例,而不仅仅是Test ,虽然通常应该避免,但在这种情况下似乎完全可以访问静态成员通过实例。

Now as many people correctly pointed out, using anonymous classes to the left is not valid Java code and probably also for the compiler not allowed without some major specification change. 现在正如许多人正确指出的那样,使用左侧的匿名类不是有效的Java代码,并且可能也不允许在没有一些主要规范更改的情况下编译器。 However, this could easily be solved by using named inner classes, so we would have 但是,这可以通过使用命名的内部类来轻松解决,所以我们可以

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

This even provides an added benefit for debugging that the inner sub class name clearly maps to the corresponding enum value. 这甚至为调试提供了额外的好处,即内部子类名称清楚地映射到相应的枚举值。

Now I understand there would have to be some minor changes, but going from anonymous to named classes and not throwing away the types seems like a small change and this looks like quite a nice feature to have, without an easy way to emulate it using overridden members or something. 现在我明白了必须进行一些细微的改动,但是从匿名到命名的类而不是丢弃这些类似乎是一个很小的改变,这看起来很不错,没有一个简单的方法来使用重写来模拟它成员或什么的。

So I was wondering why this wasn't implemented like this except time? 所以我想知道为什么除了时间之外没有像这样实现? Am I missing something crucial here that would prevent the compiler from doing this (either regarding implementation complexity or type system impossibilities) was it just not implemented to keep it simpler, because there was no time or something along those lines? 我是否遗漏了一些关键的东西,这会阻止编译器这样做(关于实现复杂性或类型系统不可能)是否只是为了保持简单而没有实现,因为没有时间或某些东西沿着这些线?

(I'm mainly looking for reasons why it was decided to implement it like this from a compiler/typesystem point of view, not for practical alternatives to this, as there are definitely a couple, though it still seems like a nice pattern to have) (我主要是寻找为什么决定从编译器/类型系统的角度来实现它的原因,而不是实际的替代方案,因为肯定有一对,尽管它似乎仍然是一个很好的模式)

Static members such as CONST_RELATED_TO_VALUE1 are members of the anonymous class for the corresponding enum value, but not members of the enum class itself. 静态成员(如CONST_RELATED_TO_VALUE1是相应枚举值的匿名类的成员,但不是枚举类本身的成员。 As with other anonymous classes, the object VALUE1 here is declared as being of type Test even though it's an instance of the anonymous subclass of Test . 与其他匿名类一样,此处的对象VALUE1被声明为Test类型,即使它是Test的匿名子类的实例。

Therefore, you can't access CONST_RELATED_TO_VALUE1 via VALUE1.CONST_RELATED_TO_VALUE1 because VALUE1 is a reference of type Test and CONST_RELATED_TO_VALUE1 is a member of the anonymous subclass but not of Test . 因此,您无法通过VALUE1.CONST_RELATED_TO_VALUE1访问CONST_RELATED_TO_VALUE1 ,因为VALUE1Test类型的引用, CONST_RELATED_TO_VALUE1是匿名子类的成员但不是Test CONST_RELATED_TO_VALUE1 can only be accessed by other members of the anonymous class. CONST_RELATED_TO_VALUE1只能由匿名类的其他成员访问。

If you want to access values defined in the anonymous class for VALUE1 , you need to have a method (say, m() ) of the enum type that you override in the anonymous class for the enum object, and that returns or provides somehow the value that you want to expose (via VALUE1.m() ). 如果要访问VALUE1的匿名类中定义的值,则需要在枚举对象的匿名类中使用枚举类型的方法(例如, m() ),并以某种方式返回或提供您要公开的值(通过VALUE1.m() )。

The enum constant are special variables that are of the type of their declaring enum type. 枚举常量是特殊变量,它们是声明枚举类型的类型。 In other words, the reference expression Test.VALUE1 is of type Test . 换句话说,引用表达式Test.VALUE1的类型为Test The type TEST does not define a variable name CONST_RELATED_TO_VALUE1 and you therefore can't access one. TEST类型没有定义变量名CONST_RELATED_TO_VALUE1 ,因此您无法访问它。

This is similar to doing 这与做的类似

class Parent {
}

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

except in your case you're trying to access a static field through a reference expression. 除非在您的情况下,您尝试通过引用表达式访问static字段。


The optional bodies of enum constants define new anonymous classes that extend the enum type. 枚举常量的可选主体定义了扩展枚举类型的新匿名类。

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

is an anonymous class which extends Test enum. 是一个扩展Test枚举的匿名类。 In case of such classes we can access its members (without help of reflection) only when we do it directly after its creation like: 在这样的类的情况下,我们可以访问其成员(没有反射的帮助)只有当我们在创建它之后直接执行它时:

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

But if we write something like: 但如果我们写下这样的话:

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

we will get compilation error since f is of type Foo which doesn't have x member declared. 我们将得到编译错误,因为fFoo类型,没有声明x成员。
And that is the problem with your enum. 这就是你的枚举问题。 What you did here: 你在这做了什么:

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

is in fact: 实际上是:

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";
}

so as you see VALUE1 (and other enum constants) are of type Test , not Test$1 (name of anonymous class given by compiler). 因此,您看到VALUE1 (和其他枚举常量)的类型为Test ,而不是Test$1 (编译器给出的匿名类的名称)。

Why Test type was chosen over Test$1 ? 为什么选择Test类型而不是Test$1 Well, it is probably because variables can't be declared with anonymous type (we can't have Test$1 foo variable) and all enum types are actually compiled into simple classes which extends Enum class so same rules must apply for its fields (constants). 好吧,这可能是因为变量不能用匿名类型声明 (我们不能使用Test$1 foo变量),并且所有枚举类型实际上都被编译成扩展Enum类的简单类,因此相同的规则必须适用于其字段(常量) )。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么Java enum不能是最终的? - Why can't a Java enum be final? 为什么匿名类在 Java 中不能有静态成员(最终成员除外)? - Why can't an anonymous class have static members (other than final ones) in Java? 为什么我不能在psvm Java中创建静态最终变量? - Why can't I create static final variable in psvm Java? 如何从 Java 应用程序中的另一个类访问在实用程序类中声明的公共最终静态列表? - How can I access to a public final static list declared into an utility class from another class in a Java application? 为什么enum的构造函数不能访问静态字段? - Why can't enum's constructor access static fields? 为什么我无法访问静态列表中的数据? - Why can't I access data from my static list? 如何用Java处理static final和static成员? - How are static final and static members handled in Java? 为什么“final static int”可以用作switch的case常量而不是“final static <your enum>” - Why “final static int” can be used as a switch's case constant but not “final static <your enum>” 为什么构造函数不能是最终的、静态的或抽象的? - Why can't constructors be final, static, or abstract? 为什么枚举常量类主体可以定义任意实例字段和方法,但不能声明静态成员或定义构造函数 - Why enum constant class body can define arbitrary instance fields, and methods, but it can't declare static members or define constructors
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM