如何帮助 Netbeans 设计器加载使用哈希映射的枚举反向查找的 JPanel-s?



这已经成为多萝西·迪克斯(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 finaldisplayMap,因为您不能在构造函数中初始化静态成员。 它必须在第一次调用时初始化,使用所示示例的一些变体:

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 可以更轻松地提供多个字符串的反向查找。

最新更新