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
            }
        }    
    }