Java中enum的静态成员的初始化
<iframe id="I0_1381823279752" style="left: 0px; top: 0px; width: 106px; height: 24px; visibility: visible; position: static;" title="+1" name="I0_1381823279752" src="https://apis.google.com/_/+1/fastbutton?usegapi=1&bsv=o&hl=en-US&origin=http%3A%2F%2Fwww.wcode.net&url=http%3A%2F%2Fwww.wcode.net%2Fplus%2Fview.php%3Faid%3D391173&gsrc=3p&ic=1&jsh=m%3B%2F_%2Fscs%2Fapps-static%2F_%2Fjs%2Fk%3Doz.gapi.zh_CN.lSYkxgKBWqM.O%2Fm%3D__features__%2Fam%3DAQ%2Frt%3Dj%2Fd%3D1%2Frs%3DAItRSTNjoa6rd0Hi1Wa13BfCczyGvikJBA#_methods=onPlusOne%2C_ready%2C_close%2C_open%2C_resizeMe%2C_renderstart%2Concircled%2Cdrefresh%2Cerefresh&id=I0_1381823279752&parent=http%3A%2F%2Fwww.wcode.net&pfname=&rpctoken=28548003" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" width="100%" data-gapiattached="true"></iframe>
时间:2009-09-02 00:00
来源:
作者:admin
点击: 67次
<!-- /info -->
Java语言规范第三版8.9规定了enum里的构造器、初始化器和初始化块中不得引用该enum中非编译时常量的静态成员域。 引用It is a compile-time error to reference a static field of an enum type that is not a compile-time constant (§15.28) from constructors, instance initializer blocks, or instance variable initial
Java语言规范第三版8.9规定了enum里的构造器、初始化器和初始化块中不得引用该enum中非编译时常量的静态成员域。
引用
It is a compile-time error to reference a static field of an enum type that is not a compile-time constant
(§15.28) from constructors, instance initializer blocks, or instance variable initializer expressions of that type. 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 itself or to an enum constant of the same type that is declared to the right of e.
规范特别指出构造器与初始化器禁止访问静态成员域是为了禁止一些循环初始化的状况。例子是:
Java Language Specification, 3rd 写道
Java枚举类型中的枚举成员是静态成员,它们会首先被静态初始化;其它成员都只能在枚举成员之后声明,如果通过初始化器(如上例)来初始化的话,则开始初始化RED时静态变量colorMap尚未被赋值。 初始化RED时要调用Color的构造器。如果允许构造器访问colorMap,就会对null调用了put()方法,于是遇到NullPointerException。 规范认为添加了上述限制后就可以让这种循环初始化的代码无法编译,从而杜绝其造成运行时异常的问题。 今天突然想起我前段时间才见到别人问过enum的初始化问题,而且就是遇到了静态初始化失败的错误。问了几个同学都说没问过我,稍微搜了下JavaEye问答频道也没看到。我还会是在哪里看到的呢,怪哉。我肯定是RP了…… 想了会儿,总算构造出了记忆中见到的那种错误: 留意第17行,被注释掉的代码如果放进来就通不过编译,跟规范里提到的要避免的状况一样。但是把同样的逻辑放到了成员方法之后,我们就成功的看到了静态初始化错误:
引用
Exception in thread "main" java.lang.ExceptionInInitializerError
at Demo.main(Demo.java:5) Caused by: java.lang.NullPointerException at PowerOfTwo.registerValue(Demo.java:26) at PowerOfTwo.<init>(Demo.java:17) at PowerOfTwo.<clinit>(Demo.java:11) ... 1 more 留意调用栈的状况。“... 1 more”没有显示出来的那个是Demo.main。它调用了PowerOfTwo枚举类型上的静态方法,引发了该类型的静态初始化(PowerOfTwo.<clinit>);其中,RED成员首先被初始化,调用构造器(PowerOfTwo.<init>);构造器则调用了成员方法registerValue来添加映射信息,访问到尚未被初始化到HashMap实例的静态成员域map,然后就出错了。 也就是说上述限制的作用很有限……跟泛型有的一拼,呵呵。 知道了问题没关系,只要问题有解决的办法就行。规范中也提供了Color例子的正确写法:
Java Language Specification, 3rd 写道
关键点是在声明了静态成员域之后,在一个静态初始化块里来完成其内容的填充,而不要急着在构造器里就去做。当然要是在构造器里先判断一下null然后做合适的初始化也不是不行,但那样代码长了而且每构造一个实例都要检查一次,麻烦。原本需要针对每个实例做的初始化可以靠values()方法遍历所有的枚举成员来做。 回到PowerOfTwo的例子,那就是改成:
|