我想创建一个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<>();
}
我发现如果mMap
为private
,则无法在实例初始值设定项中引用它。错误是Cannot make a static reference to the non-static field mMap
。在我遇到这种情况之前,我咨询了JLS §8.9.2,其中部分说明了:
构造函数,实例初始化程序块或枚举常量
e
的实例变量初始化表达式引用e
或相同类型的枚举常量是编译时错误被声明在e
的右侧。
我是否通过隐式引用FOO
自己的实例初始化程序中的FOO
来破坏此规则?这是如何编译的?它不仅可以编译,而且可以在运行时正常工作。
(我突然意识到mMap
不能是private
因为我隐式创建了一个匿名子类,它不能引用其超类中的private
字段。这有点奇怪因为枚举是隐含的final
...)
答案 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