这已经成为多萝西·迪克斯(Dorothy Dix),因为我在撰写这个问题时找到了问题的根源。 尽管如此,我还是决定发布它,因为它让我们办公室里的每个人都难倒了,我在谷歌或堆栈溢出搜索中找不到任何有用的东西。 这是一个很好的例子,一旦你问了正确的问题,你可能会看到光明。
虽然标题听起来很复杂,但这是一个非常简单的问题,似乎没有答案。 其中心是一条enum
:
Status
public enum Status
{
INVALID ( "INVALID" ),
ISSUED ( "Issued" ),
CANCELLED ( "Cancelled");
private final String displayName;
private final static Map<String,Status> displayMap = new HashMap( 4 );
private Status( String display ){
this.displayName = display;
mapDisplayName( this.displayName, this );
}
public String getDisplayName(){
return displayName;
}
public static Status parseString( String statusStr ) {
return displayMap.get( statusStr );
}
private static void mapDisplayName( final String displayName, final Status state ){
displayMap.put( displayName, state );
}
}
当然,这个想法是将displayMap
用作反向查找。 与getDisplayName()
方法完全无关。
此枚举的getDisplayName()
调用在子面板中用于初始化与组合框一起使用的静态数组,例如:
public class JPanelStatus extends javax.swing.JPanel {
private final String[] STATUS_LABELS = {
Status.ISSUED .getDisplayName(),
Status.CANCELLED .getDisplayName()
};
public JPanelStatus(){
initComponents();
:
jComboBoxStatus.setModel( new DefaultComboBoxModel<>( STATUS_LABELS ) );
:
}
:
}
在主 JPanel 中引用。 当我在 Netbeans 设计器中查看此JPanelStatus
子面板时,它工作正常。 [Preview Design
] 函数也是如此。
但是,当我加载主窗体时,它失败并且异常显示初始化失败:
java.lang.NoClassDefFoundError: Could not initialize class au.com.project.State
at au.com.project.client.JPanelStatus.<init>(JPanelStatus.java:35)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
... ... ...
Netbeans IDE 日志添加了以下额外信息 :-p
INFO: Could not initialize class au.com.project.State
通过消除的过程 - 注释掉不相关的代码 - 我发现一旦我在状态枚举中注释掉HashMapput()
调用,表单就会加载。
">这很有意思。" 它看起来像是put()
的副作用. 在另一个地方,它是一个小的 Spock,它很快就从命令行给了我同样的错误,没有 JPanel 和 Netbeans。
该错误是由我引起的,我试图从枚举构造函数中使用哈希映射。 它不会按书面形式工作。
所以我更改了标题以击中真正的问题 - 实际上是如何使用HashMap对枚举进行反向查找?
问题来自由于枚举的初始化方式,HashMap 如何在Status
枚举中声明。
Java 枚举中的第一件事必须是值列表,此处为:"无效"、"已发出"和"取消"。 每个人都需要知道的下一件事是,在对象创建(类或枚举)期间首先运行一个秘密的Java阶段。 Init 是愚蠢的,通过声明式代码线性运行,先到先得。
枚举的前 3 个语句调用构造函数 -- 这意味着语句:
private final static Map<String,Status> displayMap = new HashMap( 4 );
尚未执行,displayMap
为空。 此外,static { }
块在相同的 1-2-3 中执行-...序列,也不起作用。
遗憾的是,Netbeans/Designer 堆栈跟踪或 IDE 日志都没有报告 NullPointerException -- 单元测试报告了 NullPointerException。 一旦你有了NPE,它就会集中注意力。displayMap
在进行第一次构造函数调用时未初始化。
解决方案:不能static final
displayMap,因为您不能在构造函数中初始化静态成员。 它必须在第一次调用时初始化,使用所示示例的一些变体:
Status
public enum Status
{
INVALID ( "INVALID" ),
ISSUED ( "Issued" ),
CANCELLED ( "Cancelled");
private static Map<String,Status> displayMap;
private Status( String display ){
this.displayName = display;
mapDisplayName( this.displayName, this );
}
:
private static void mapDisplayName( final String displayName, final Status state ){
if( null == displayMap ){
displayMap = new HashMap( 7 );
}
displayMap.put( displayName, state );
}
}
然后一切都运行得非常顺利。
注意事项:
不要在displayMap声明中分配null
- 这是适得其反的:
if( null == displayMap ){...}
块在第一次调用 Enum 构造函数期间成功分配 HashMap。- 处理完所有枚举值声明后。
- Init 将调用已声明变量的任何初始化。
- 如果声明了
displayMap = null;
,它将用一个新的空哈希映射替换填充的 HashMap(具有 3 x 值)。嘎��
可能相关的问题:
- 反向查找每个键/常量具有多个值的 Java 枚举?我觉得 HashMap 可以更轻松地提供多个字符串的反向查找。