【问题标题】:Cannot access static field within enum initialiser无法访问枚举初始化程序中的静态字段
【发布时间】:2011-05-15 18:33:31
【问题描述】:

在这段代码中我得到一个编译器错误,请参阅注释:

 public enum Type {
   CHANGESET("changeset"),
   NEW_TICKET("newticket"),
   TICKET_CHANGED("editedticket"),
   CLOSED_TICKET("closedticket");

   private static final Map<String, Type> tracNameMap = new HashMap<String, Type>();

   private Type(String name) {
    tracNameMap.put(name, this); // cannot refer to static field within an initializer
   }

   public static Type getByTracName(String tn) {
    return tracNameMap.get(tracNameMap);
   }

  }

有没有办法让这项工作发挥作用,通过其中一个字段从 Map 获取枚举值?

【问题讨论】:

  • 可能值得说明地图的“键”是否始终是枚举的“名称”。如果是这样,那么您将如何做到这一点会有很大的不同。

标签: java enums static initializer


【解决方案1】:

这里的地图可能有点矫枉过正。除非您计划拥有四个以上的枚举值,否则您可以通过简单地遍历有效字符串并返回正确的字符串来实现 getByTracName(String tn)。如果映射键始终是枚举名称,那么您可以这样做:

public enum Type {
CHANGESET,
NEW_TICKET,
TICKET_CHANGED,
CLOSED_TICKET;

private static final Map<String, Type> tracNameMap = new HashMap<String, Type>();
static {
    for (Type t:Type.values()) {
        tracNameMap.put(t.name(), t);
    }
}
public static Type getByTracName(String tn) {
    return tracNameMap.get(tracNameMap);
}

}

或者你可以这样做:

public static Type getByTracName(String tn) {
  return Enum.valueOf(Type.class,tn);
}

【讨论】:

  • Enum.valueOf(Type.class,tn) 应该写成Type.valueOftn)
【解决方案2】:

哈,好笑!就在几天前,我偶然发现了这个。

来自 Java 语言规范,第三版,第 8.9 节:

从该类型的构造函数、实例初始化块或实例变量初始化表达式引用不是编译时常量(第 15.28 节)的枚举类型的静态字段是编译时错误。枚举常量 e 的构造函数、实例初始化块或实例变量初始化表达式引用自身或在 e 右侧声明的相同类型的枚举常量是编译时错误。

讨论

如果没有这条规则,显然合理的代码将在运行时失败,因为枚举类型固有的初始化循环性。 (在任何具有“自类型”静态字段的类中都存在循环。)以下是可能失败的代码示例:

enum Color {
        RED, GREEN, BLUE;
        static final Map<String,Color> colorMap = 
        new HashMap<String,Color>();
        Color() {
            colorMap.put(toString(), this);
        }
    } 

此枚举类型的静态初始化会抛出 NullPointerException,因为当枚举常量的构造函数运行时静态变量 colorMap 未初始化。上面的限制确保这样的代码不会编译。

请注意,该示例可以轻松重构以正常工作:

enum Color {
        RED, GREEN, BLUE;
        static final Map<String,Color> colorMap = 
        new HashMap<String,Color>();
        static {
            for (Color c : Color.values())
                colorMap.put(c.toString(), c);
        }
    } 

重构后的版本显然是正确的,因为静态初始化从上到下发生。

【讨论】:

    【解决方案3】:

    我会使用Reversible Enum Pattern

    ReversibleEnum.java

    /**
     * <p>
     * This interface defines the method that the {@link Enum} implementations
     * should implement if they want to have the reversible lookup functionality.
     * i.e. allow the lookup using the code for the {@link Enum} constants.
     * </p>
     * @author Atif Khan
     * @param < E >
     *          The value of Enum constant
     * @param < V >
     *          The Enum constant to return from lookup
     */
    public interface ReversibleEnum< E, V >
    {
      /**
       * <p>
       * Return the value/code of the enum constant.
       * </p>
       * @return value
       */
      public E getValue();
    
      /**
       * <p>
       * Get the {@link Enum} constant by looking up the code in the reverse enum
       * map.
       * </p>
       * @param  E - code
       * @return V - The enum constant
       */
      public V reverse( E code );
    }
    

    ReverseEnumMap.java

    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * <p>
     * A utility class that provides a reverse map of the {@link Enum} that is keyed
     * by the value of the {@link Enum} constant.
     * </p>
     * @author Atif Khan
     * @param < K >
     *          The class type of the value of the enum constant
     * @param < V >
     *          The Enum for which the map is being created
     */
    public class ReverseEnumMap< K, V extends ReversibleEnum< K, V >>
    {
      private final Map< K, V > mReverseMap = new HashMap< K, V >();
    
      /**
       * <p>
       * Create a new instance of ReverseEnumMap.
       * </p>
       * @param valueType
       */
      public ReverseEnumMap( final Class< V > valueType )
      {
        for( final V v : valueType.getEnumConstants() ) {
          mReverseMap.put( v.getValue(), v );
        }
      }
    
      /**
       * <p>
       * Perform the reverse lookup for the given enum value and return the enum
       * constant.
       * </p>
       * @param enumValue
       * @return enum constant
       */
      public V get( final K enumValue )
      {
        return mReverseMap.get( enumValue );
      }
    }
    

    你可以改变 Type.java 如下:

    public enum Type implements ReversibleEnum< String, Type >  {
      CHANGESET( "changeset" ),
      NEW_TICKET( "new" ),
      TICKET_CHANGED( "changed" ),
      CLOSED_TICKET( "closed" );
    
      private String mValue;  
    
      private static final ReverseEnumMap< String, Type > mReverseMap = new ReverseEnumMap< String, Type >( Type.class );  
    
      Type(final String value)   
      {  
        mValue = value;  
      }  
    
      public final String getValue()   
      {  
        return mValue;  
      }  
    
      public Type reverse( final String value )  
      {  
        return mReverseMap.get( value );  
      } 
    } 
    

    【讨论】:

    • 我必须说有点过度设计。 :-)
    【解决方案4】:

    这个怎么样;不需要您在两个地方进行代码更改,这在 IMO 中很容易出错:

    enum Type {
    
        CHANGESET("changeset"),
        NEW_TICKET("newticket"),
        TICKET_CHANGED("editedticket"),
        CLOSED_TICKET("closedticket");
    
        private static final Map<String, Type> tracNameMap =
                                          new HashMap<String, Type>();
    
        private final String name;
    
        public Type typeForName(final String name) {
            if (tracNameMap.containsKey(name)) {
                return tracNameMap.get(name);
            } else {
                for (final Type t : Type.values()) {
                    if (t.name.equals(name)) {
                        tracNameMap.put(name, t);
                        return t;
                    }
                }
                throw new IllegalArgumentException("Invalid enum name");
            }
        }
    
        private Type(String name) {
            this.name = name;
        }
    
    }
    

    【讨论】:

      【解决方案5】:

      我自己的解决方法,虽然它需要重复所有枚举值:

          public enum Type {
              CHANGESET,
              NEW_TICKET,
              TICKET_CHANGED,
              CLOSED_TICKET;
      
              private static final Map<String, Type> tracNameMap = new HashMap<String, Type>();
              static {
                  tracNameMap.put("changeset", CHANGESET);
                  tracNameMap.put("newticket", NEW_TICKET);
                  tracNameMap.put("editedticket", TICKET_CHANGED);
                  tracNameMap.put("closedticket", CLOSED_TICKET);
              }
              public static Type getByTracName(String tn) {
                  return tracNameMap.get(tracNameMap);
              }
      
          }
      

      【讨论】:

      猜你喜欢
      • 2023-03-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多