枚举常量特定的类体是静态的还是非静态的?

2024-01-26

我有一个枚举类型类:

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身体。为什么它在static范围?如何摆脱它?

我知道添加一个static关键字printMe(){...}解决了问题,但我有兴趣知道如果我想保留是否还有其他方法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-具体的static变量,但最终出现错误:

字段“name”不能在非静态内部类型中声明为静态。

为什么我不能在里面定义静态常量PLUS if PLUS是匿名类吗?这两个错误消息听起来相互矛盾,正如第一个错误消息所示PLUS(){...} has static上下文,而第二条错误消息显示PLUS(){...} has 非静态语境。我现在更迷茫了。


嗯,这是一个奇怪的案例。

看来问题是:

  • 在这种情况下,私人成员应该是可以访问的 (6.6.1 http://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.6.1.):

    否则,声明成员或构造函数private,并且当且仅当访问发生在包含成员或构造函数声明的顶级类的主体内时才允许访问。

  • 但是,私有成员不会被继承(8.2 http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.2):

    声明的类的成员private不被该类的子类继承。

  • 所以,printMe不是匿名子类的成员,编译器在超类中搜索它*Operation (15.12.1 http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.1):

    如果存在封闭类型声明该方法是其中的成员,令 T 为最里面的此类类型声明。要搜索的类或接口是 T。

    这种搜索策略称为“梳状规则”。在查找封闭类及其超类层次结构中的方法之前,它会有效地查找嵌套类的超类层次结构中的方法。

  • 这就是奇怪的地方。因为printMe在一个类中找到还附上 PLUS,调用该方法的对象被确定为一个封闭实例Operation,不存在(15.12.4.1 http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.4.1):

    否则,令 T 为该方法所属的封闭类型声明,并让n是一个整数,使得 T 是n'th 词法封闭类的类型声明,其声明立即包含方法调用。目标参考是n'第一个词法封闭实例this.

    如果出现以下情况,则为编译时错误n'第一个词法封闭实例this不存在。

简而言之,因为printMe只是其中的成员Operation(并且不是继承的),编译器被迫调用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;
}

这两个错误消息听起来互相矛盾[...]。

是的,这是该语言的一个令人困惑的方面。一方面,匿名类永远不是静态的(15.9.5 http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.9.5),另一方面,匿名类表达式可以出现在静态上下文中因此没有封闭实例(8.1.3 http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.1.3).

匿名类始终是内部类;从来没有static.

内部类的实例I其声明发生在静态上下文中,没有词法封闭的实例。

为了帮助理解它是如何工作的,这里有一个格式化的示例:



class Example {
    public static void main(String... args) {
        new Object() {
            int i;
            void m() {}
        };
    }
}
  

一切都在italics是静态上下文。从表达式派生的匿名类bold被认为是内部的和非静态的(但没有封闭的实例Example).

由于匿名类是非静态的,因此它不能声明静态非常量成员,尽管它本身是在静态上下文中声明的。


*除了稍微掩盖了事情之外,事实是Operation是一个枚举是完全不相关的(8.9.1 http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.1):

枚举常量的可选类主体隐式定义了一个匿名类声明,该声明扩展了直接封闭的枚举类型。类主体由匿名类的通常规则管理[...]。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

枚举常量特定的类体是静态的还是非静态的? 的相关文章

随机推荐