为什么这个枚举编译?

Why does this enum compile?

我想创建一个 enum,其中每个常量都有一个与之关联的 Map。我通过给每个常量一个实例初始化器来完成这个,就像这样:

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<>();
}

我发现如果mMapprivate,它不能在实例初始化器中被引用。错误是 Cannot make a static reference to the non-static field mMap。在我想到这个原因之前,我咨询了JLS §8.9.2,其中部分说:

It is a compile-time error for the constructors, instance initializer blocks, or instance variable initializer expressions of an enum constant e to refer to e or to an enum constant of the same type that is declared to the right of e.

我在 FOO 自己的实例初始化器中隐式引用 FOO 是不是违反了这条规则?这是如何编译的?它不仅可以编译,而且可以在运行时正常工作。

(我想到 mMap 不能是 private 因为我隐式创建了一个匿名子类,它不能引用其超类中的 private 字段。这有点本身很奇怪,因为枚举是隐式的 final...)

It is a compile-time error for the constructors, instance initializer blocks, or instance variable initializer expressions of an enum constant e to refer to e or to an enum constant of the same type that is declared to the right of e.

这里的说明只是表示不能通过name引用,因为e引用的字段还没有初始化。这并不意味着您无法访问 this.

它与任何其他初始化程序(如 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

I found that if mMap is private, it cannot be referenced in the instance initializer.

您可以将呼叫限定为 super.mMap.put(...);。私有 mMap 不被继承,但可以从内部 class 访问。 。简而言之,简单名称 mMap 指的是 Derp.

的不存在的外部实例

我们可以用 (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 是静态的,所以没有外部实例——因此编译器错误。

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

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