如何通过重写方法来使用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
字段,只有一种方法是合法的:
-
private
成员不是inheritance的。 因此,当在超类中定义private
字段时,您不能从this
实例访问private
字段。 -
对于内部类,外部类的成员即使是
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(文件)结尾。
我们的想法是分开代码。 使用接口作为基类,然后将每个策略实现为单独的子类。 很干净。