无法访问枚举初始化程序中的静态字段

时间:2010-11-25 15:06:46

标签: java enums static initializer

在此代码中,我收到编译器错误,请参阅注释:

 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 个答案:

答案 0 :(得分:9)

地图在这里可能有些过分。除非您计划使用多于四个枚举值,否则只需迭代有效字符串并返回正确的字符串即可实现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);
}

答案 1 :(得分:7)

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

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

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

答案 2 :(得分:2)

我会使用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 );  
  } 
} 

答案 3 :(得分:1)

这个怎么样;不需要你在两个地方进行代码更改,这是一种容易出错的错误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;
    }

}

答案 4 :(得分:0)

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

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

    }