为什么这个枚举会编译?

2022-09-04 04:28:55

我想创建一个每个常量都有一个与之关联的位置。我通过为每个常量指定一个实例初始值设定项来实现此目的,如下所示:enumMap

import java.util.HashMap;
import java.util.Map;

public enum Derp {
    FOO {{
            mMap.put("bar", 1);
        }};

    // cannot be private
    protected final Map<String, Integer> mMap = new HashMap<>();
}

我发现如果是,它不能在实例初始值设定项中引用。错误为 。在我遇到这种情况的原因之前,我查阅了JLS §8.9.2,其中部分内容如下:mMapprivateCannot make a static reference to the non-static field mMap

对于枚举常量的构造函数、实例初始值设定项块或实例变量初始值设定项表达式,引用枚举常量或声明在 右侧的相同类型的枚举常量,这是一个编译时错误。eee

我不是通过在 自己的实例中隐式引用 intializer 来打破此规则吗?它是如何编译的?它不仅可以编译,而且在运行时可以正常工作。FOOFOO

(我突然想到这不可能,因为我隐式创建了一个匿名子类,该子类无法引用其超类中的字段。这本身有点奇怪,因为枚举是隐含的...)mMapprivateprivatefinal


答案 1

对于枚举常量的构造函数、实例初始值设定项块或实例变量初始值设定项表达式,引用枚举常量或声明在 右侧的相同类型的枚举常量,这是一个编译时错误。eee

此处的规范只是意味着您无法按名称引用,因为 by 引用的字段尚未初始化。这并不意味着您无法访问 。ethis

它基本上与任何其他初始值设定项(如 )的规则相同。int x = x;

我们可以用一个像(Ideone)这样的例子来解释为什么:

enum Example {
    INSTANCE {{
        subversion();
    }};

    static void subversion() {
        System.out.println(INSTANCE);
    }

    public static void main(String[] args) {
        System.out.println(INSTANCE);
    }
}

哪些输出

null
INSTANCE

我发现如果是私有的,则无法在实例初始值设定项中引用它。mMap

您可以将呼叫限定为 。私有不是继承的,但可以从内部类访问。我在这里也介绍了这一点。简而言之,简单名称是指 不存在的外部实例。super.mMap.put(...);mMapmMapDerp

我们可以用一个例子来验证这种情况,比如(Ideone):

class Example {
    private int x;

    class Inner extends Example {{
        x = 1;       // refers to the outer instance
        super.x = 2; // refers to the inner instance
    }}

    public static void main(String[] args) {
        Example outer = new Example();
        Example inner = outer.new Inner();
        System.out.println(outer.x); // prints 1
        System.out.println(inner.x); // prints 2
    }
}

除非您的情况是静态的,因此没有外部实例 , 因此编译器错误。FOO


答案 2

这是因为它是自己的匿名子类 - 在创建时已经存在。FOODerpFOO

public class Enums {
    public enum Derp {
        FOO {{
            mMap.put("bar", 1);
        }};

        // cannot be private
        protected final Map<String, Integer> mMap = new HashMap<>();
    }

    public static void main(String[] args) {
        System.out.println(Derp.class);
        System.out.println(Derp.FOO.getClass());
    }
}

class Enums$Derp
class Enums$Derp$1