[英]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 现在我明白了, 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);
}
}
}
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
. 很明显,我们在运行时为VALUE1
和VALUE2
提供了适当的专用子类。 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. 但是看起来我们丢失有关VALUE1
和VALUE2
的具体类型信息的原因是编译器为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
,因为VALUE1
是Test
类型的引用, 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. 我们将得到编译错误,因为f
是Foo
类型,没有声明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.