为什么这个枚举编译?

时间:2015-04-15 03:16:30

标签: java enums

我想创建一个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,其中部分说明了:

  

构造函数,实例初始化程序块或枚举常量e的实例变量初始化表达式引用e或相同类型的枚举常量是编译时错误被声明在e的右侧。

我是否通过隐式引用FOO自己的实例初始化程序中的FOO来破坏此规则?这是如何编译的?它不仅可以编译,而且可以在运行时正常工作。

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

2 个答案:

答案 0 :(得分:4)

  

构造函数,实例初始化程序块或枚举常量e的实例变量初始化表达式引用e或相同类型的枚举常量是编译时错误被声明在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

  

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

您可以将通话限定为super.mMap.put(...);。私有mMap不是继承的,但可以从内部类访问。 I also covered this here。简而言之,简单名称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是静态的,所以没有外部实例 - 因此编译错误。

答案 1 :(得分:1)

这是因为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());
    }
}
  

类枚举$ Derp
  class Enums $ Derp $ 1