Java 枚举常量特定的类主体是静态的还是非静态的?
我有一个枚举类型类:Java 枚举常量特定的类主体是静态的还是非静态的?,java,enums,static,anonymous-inner-class,Java,Enums,Static,Anonymous Inner Class,我有一个枚举类型类: public enum Operation { PLUS() { @Override double apply(double x, double y) { // ERROR: Cannot make a static reference // to the non-static method printMe()... printMe(x);
public enum Operation {
PLUS() {
@Override
double apply(double x, double y) {
// ERROR: Cannot make a static reference
// to the non-static method printMe()...
printMe(x);
return x + y;
}
};
private void printMe(double val) {
System.out.println("val = " + val);
}
abstract double apply(double x, double y);
}
如上所述,我定义了一个enum
类型,其值为PLUS
。它包含一个恒定的特定实体。在它的主体中,我试图调用printMe(val)代码>,但我得到了编译错误:
无法对非静态方法printMe()进行静态引用
为什么会出现这个错误?我的意思是,我正在重写PLUS
body中的抽象方法。为什么它在静态范围内?如何摆脱它
我知道在printMe(){…}
上添加static
关键字可以解决问题,但我想知道如果我想保持printMe()
非静态,是否还有其他方法
另一个问题,与上面的问题非常相似,但这次错误消息听起来与此相反,即PLUS(){…}
具有非静态上下文:
public enum Operation {
PLUS() {
// ERROR: the field "name" can not be declared static
// in a non-static inner type.
protected static String name = "someone";
@Override
double apply(double x, double y) {
return x + y;
}
};
abstract double apply(double x, double y);
}
我试图声明一个PLUS
特定的静态变量,但最终出现错误:
字段“name”不能在非静态内部类型中声明为静态
如果PLUS
是匿名类,为什么我不能在PLUS
中定义静态常量?这两条错误消息听起来相互矛盾,因为第一条错误消息说PLUS(){…}
具有静态上下文,而第二条错误消息说PLUS(){…}
具有非静态上下文。我现在更困惑了。我想我对错误的性质没有答案,但也许我可以为讨论贡献一点力量
当Java编译器编译枚举代码时,它会生成一个合成类,如下所示:
class Operation {
protected abstract void foo();
private void bar(){ }
public static final Operation ONE;
static {
ONE = new Operation() {
@Override
protected void foo(){
bar();
}
};
}
}
通过在一个枚举类中运行javap,可以验证枚举代码是否与此类似
上面的代码给出了与枚举上得到的错误完全相同的错误:“错误:无法从静态上下文引用非静态方法bar()
因此在这里,编译器认为您无法从定义匿名类的静态上下文中调用bar()
方法,这是一个实例方法
这对我来说毫无意义,它应该是可访问的,或者被拒绝访问,但错误似乎并不准确。我仍然感到困惑,但这似乎是实际发生的事情
如果编译器说匿名类没有访问foo
的权限,因为它在其父类上是私有的,那么这就更有意义了,但是编译器会触发另一个错误 printMe
不应是private
,因为您使用PLUS派生了一个新的匿名类
protected void printMe(double val) {
至于错误的性质,enum/enum有点像伪影;现在我想不起来了:内部类可能会访问私有内容…使printMe方法为静态可以解决编译错误:
private static void printMe(long val) {
System.out.println("val = " + val);
}
PLUS()的类型是什么?
这是枚举操作的基本类型
如果您想将它与java类
进行比较,它基本上是一个内部相同类的对象
现在enum操作
具有抽象方法apply
,这意味着任何此类(即操作)都应该实现此方法。到目前为止还不错
现在是出现错误的棘手部分
如您所见,PLUS()
基本上是一种操作Operation
有一个private
方法printMe()
这意味着只有enum Operation
本身才能看到它,包括子枚举在内的任何其他枚举(就像java中的子类和超类一样)。此外,此方法不是静态的,也就是说,除非实例化该类,否则无法调用它。所以你有两个选择来解决这个问题
按照编译器的建议,将printMe()方法设为静态
使方法受保护
,以便任何子枚举
继承该方法
在这种情况下,使其静态工作的原因是自动合成存取器功能。如果它是私有静态的,您仍然会得到以下编译器警告
无法从类型操作访问封闭方法printMe(double)
通过合成访问器方法进行仿真
在这种情况下唯一不起作用的是私有非静态。所有其他安全工作,例如私有静态、受保护的非静态等。正如其他人提到的,PLUS是一个操作的实现,因此私有技术不起作用,Java会自动为您修复自动合成访问器功能。更新后的问题很容易回答。匿名类永远不允许是静态成员
至于您最初的问题,了解发生了什么最清楚的方法是尝试this.printMe()代码>取而代之。这样,错误消息就更容易理解,并给出了真正的原因printMe()代码>不起作用:
'printMe(double)' has private access in 'Operation'
不能使用printMe
的原因是它是private
,并且此引用的编译时类型是操作的匿名扩展类,而不是操作本身(请参见Edwin Dalorzo的回答)。当您只编写printMe()时,会收到不同的错误消息因为出于某种原因,编译器甚至没有意识到您试图在this
上调用一个实例方法。如果您试图在任何实例上调用printMe
,它会给出一条有意义的错误消息(即,好像它是一个静态方法)。如果通过编写Operation.printMe()使错误消息显式,则错误消息不会更改代码>
解决这个问题的两种方法是使printMe
受到保护,或者编写
((Operation) this).printMe();
这是一个奇怪的案例
看来
@Override
double apply(double x, double y) {
// now the superclass is searched
// but the target reference is definitely 'this'
// vvvvvv
super.printMe(x);
return x + y;
}
class Example {
public static void main(String... args) {
new Object() {
int i;
void m() {}
};
}
}
public class Outer {
protected void protectedMethod() {
}
private void privateMethod() {
}
public class Inner {
public void method1() {
protectedMethod(); // legal
privateMethod(); // legal
}
}
public static class Nested {
public void method2() {
protectedMethod(); // illegal
privateMethod(); // illegal
}
}
public static class Nested2 extends Outer {
public void method3() {
protectedMethod(); // legal
privateMethod(); // illegal
}
}
}