我想知道为什么,虽然在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
的静态最终实例,因此没有这些字段,但理论上应该是信息在编译时可用,可以通过little 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
的具体类型信息的原因是编译器为{Test
生成的基本枚举类VALUE3
生成静态枚举值的方式。 1}}:所有成员都是Test
类型,具体类型被丢弃。
然而,在我看来,如果编译器只是保留那些类型
public static final Test$1 Test.VALUE1
public static final Test$2 Test.VALUE2
public static final Test Test.VALUE3
所有周围的代码仍然有用。此外,我们还可以执行我最初尝试的操作并通过CONST_RELATED_TO_VALUE1
访问Test.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
这甚至为调试提供了额外的好处,即内部子类名称清楚地映射到相应的枚举值。
现在我明白必须进行一些细微的改动,但是从匿名到命名的类而不是丢弃类型似乎是一个很小的改变,这看起来很不错,没有简单的方法来模拟它使用被覆盖的成员或其他东西。
所以我想知道为什么除了时间之外没有像这样实现?我是否遗漏了一些关键的东西,这会阻止编译器这样做(关于实现复杂性或类型系统不可能)是不是为了保持简单而没有实现,因为这些行没有时间或东西?
(我主要是寻找为什么决定从编译器/类型系统的角度来实现它的原因,而不是实际的替代方案,因为肯定有一对,尽管它看起来仍然很好模式有)
答案 0 :(得分:7)
CONST_RELATED_TO_VALUE1
之类的静态成员是相应枚举值的匿名类的成员,但不是枚举类本身的成员。与其他匿名类一样,此处的对象VALUE1
被声明为类型Test
,即使它是Test
的匿名子类的实例。
因此,您无法通过CONST_RELATED_TO_VALUE1
访问VALUE1.CONST_RELATED_TO_VALUE1
,因为VALUE1
是Test
类型的引用,CONST_RELATED_TO_VALUE1
是匿名子类的成员但不是Test
。 CONST_RELATED_TO_VALUE1
只能由匿名类的其他成员访问。
如果要访问VALUE1
的匿名类中定义的值,则需要在enum的匿名类中重写枚举类型的方法(例如,m()
) object,并以某种方式返回或提供您想要公开的值(通过VALUE1.m()
)。
答案 1 :(得分:3)
枚举常量是特殊变量,它们是声明枚举类型的类型。换句话说,引用表达式Test.VALUE1
的类型为Test
。类型TEST
未定义变量名称CONST_RELATED_TO_VALUE1
,因此您无法访问变量名称。{/ p>
这类似于做
class Parent {
}
class Child extends Parent {
public Object field = new Object();
}
...
Parent ref = new Child();
System.out.println(ref.field); // compilation error
除非您在案例中尝试通过引用表达式访问static
字段。
枚举常量的可选主体定义了扩展枚举类型的新匿名类。
答案 2 :(得分:2)
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类的简单类,因此必须对其字段(常量)应用相同的规则。