如何通过重写方法来使用java enum中的字段?

任务是用java enum实现漂亮的策略设计模式:

 public enum MyEnum { FIRST { @Override public String doIt() { return "1: " + someField; //error } }, SECOND { @Override public String doIt() { return "2: " + someField; //error } }; private String someField; public abstract String doIt(); } 

但是当提到someField我得到了

无法对someField的非静态字段进行静态引用。

有什么不对,有可能做得更好吗?

专门的enum只不过是具有内部类语义的子类。 如果在编译后查看字节代码,您会注意到编译器只插入访问器方法来读取私有字段,但任何专用的枚举都被编译为自己的类。 您可以将您的enum视为实现为:

 public abstract class MyEnum { private static class First extends MyEnum { @Override public String doIt() { return "1: " + someField; //error } } private static class Second extends MyEnum { @Override public String doIt() { return "2: " + someField; //error } } public static final MyEnum FIRST = new First(); public static final MyEnum SECOND = new Second(); private String someField; public abstract String doIt(); } 

如您所见,发生了相同的编译器错误。 实际上,您的问题与enum无关,而与内部语义无关。

但是,您发现了编译器猜测您的代码意图的边缘情况,并试图警告您,您的意图是非法的。 通常, someField字段对任何专用enum都是可见的。 但是,有两种方法可以从内部类访问private字段,只有一种方法是合法的:

  1. private成员不是inheritance的。 因此,当在超类中定义private字段时,您不能从this实例访问private字段。

  2. 对于内部类,外部类的成员即使是private也可以访问。 这是由编译器通过将访问器方法插入外部类来实现的,这些外部类通过访问器方法公开private字段。 只有在内部类是非static下才能访问非static字段。 对于enum ,内部类总是static

后来的条件是编译器抱怨的:

无法对someField的非静态字段进行静态引用

您正尝试从static内部类访问非static字段。 即使字段因内部类语义而在技术上可见,这也是不可能的。 您可以通过例如从超类中读取它来明确指示编译器访问该值:

 public String doIt() { MyEnum thiz = this; return thiz.someField; } 

现在,编译器知道您正在尝试访问可见(外部)类型的成员,而不是错误地访问(非静态)外部类实例(不存在)的someField字段。 (类似地,您可以编写super.someField ,它表达了您希望沿着inheritance链走下去并且不访问外部实例的字段的相同想法。)然而,更简单的解决方案是简单地使字段protected 。 这样编译器对inheritance可见性感到高兴并编译原始设置。

如果您使someField受保护而不是私有或使用super.someField您将能够访问它。

someField是私有的,删除私有修饰符或将其移动到抽象类中。

从子类无法访问私有字段,这正是您在每个实例的基础上实现MyEnum.doIt()抽象方法时所执行的MyEnum.doIt() 。 将其更改为protected ,它将起作用。

当枚举是静态变量时,someField是一个私有变量。 您不能以这种方式将非静态变量分配给静态变量。

显然问题是当你说:

 public enum MyEnum { ... public abstract String doIt(); } 

它隐含地需要枚举成为一个abstract “类”,因为你必须为它提供一个实现。 所以,当你说

 FIRST { @Override public String doIt() { return "1: " + this.someField; //error } } 

它给出了一个错误,因为您正在尝试访问“基类” MyEnum的私有字段,并且由于它是私有的,因此从隐式创建的匿名子类MyEnum它。 因此, protected在子类中是可见的,因此它解决了问题。

Stack Overflow上有一些关于这个问题的问题,比如Singletons,Enums和匿名内部类或者为什么我可以匿名子类化enum而不是最终类? 。

编辑:显然不是这个语句中的所有内容都是正确的,因为虽然this.someField不起作用,因为从子类this.someField不到该字段,但它可以作为super.someField访问。 这是我以前从未见过的现象,现在将尝试调查。

你能做的是以下几点:

 public enum MyEnum { FIRST,SECOND; private String someField; public String doIt(){ switch(this){ case FIRST: return "1: " + someField; break; case SECOND: return "2: " + someField; break; } } } 

这样,你仍然inheritance了Enum ,你可以使用MyEnum.values()和其他来自派生Enum特权。

我不会使用枚举实现策略模式。 所有代码都以相同的unti(文件)结尾。

我们的想法是分开代码。 使用接口作为基类,然后将每个策略实现为单独的子类。 很干净。