代码之家  ›  专栏  ›  技术社区  ›  Bart van Heukelom

无法访问枚举初始化器内的静态字段

  •  2
  • Bart van Heukelom  · 技术社区  · 15 年前

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

     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 在它的某个领域?

    5 回复  |  直到 15 年前
        1
  •  9
  •   DJClayworth    15 年前

    这张地图可能太夸张了。除非您计划拥有四个以上的枚举值,否则只需迭代有效的字符串并返回正确的字符串,就可以实现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);
    }
    
        2
  •  7
  •   T-Bull    15 年前

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

    从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
  •  2
  •   mtpettyp    15 年前

    我会用 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
  •  1
  •   Sanjay T. Sharma    15 年前

    怎么样;不需要在两个地方进行代码更改,这是一种容易出错的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
  •  0
  •   Bart van Heukelom    15 年前

    我自己的解决方案,尽管它需要重复所有枚举值:

        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);
            }
    
        }