Не удается получить доступ к статическому полю в инициализаторе перечисления - PullRequest
2 голосов
/ 25 ноября 2010

В этом коде я получаю ошибку компилятора, см. Комментарий:

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

  }

Есть ли способ заставить эту работу получить значение enum из Map одним из его полей?

Ответы [ 5 ]

9 голосов
/ 25 ноября 2010

Карта здесь, вероятно, излишня. Если вы не планируете иметь более четырех значений enum, вы можете реализовать 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);
}
7 голосов
/ 12 декабря 2010

Ха, смешно!Всего несколько дней назад я наткнулся на это.

Из спецификации языка Java, третье издание, раздел 8.9:

Ссылка на ошибку во время компиляциистатическое поле типа enum, которое не является константой времени компиляции (§15.28) из конструкторов, блоков инициализатора экземпляра или выражений инициализатора переменной экземпляра этого типа.Это ошибка времени компиляции, когда конструкторы, блоки инициализатора экземпляра или выражения инициализатора переменной экземпляра константы перечисления e ссылаются на себя или на константу перечисления того же типа, которая объявлена ​​справа от e.

Обсуждение

Без этого правила очевидно разумный код не будет работать во время выполнения из-за цикличности инициализации, присущей типам enum.(В любом классе с «самоподписанным» статическим полем существует цикличность.) Вот пример кода, который потерпит неудачу:

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 голосов
/ 25 ноября 2010

Я бы использовал шаблон обратимого перечисления :

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 );  
  } 
} 
1 голос
/ 25 ноября 2010

Как насчет этого;не требует внесения изменений в код в двух местах, что является своего рода подверженным ошибкам 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;
    }

}
0 голосов
/ 25 ноября 2010

Мой собственный обходной путь, хотя он требует повторения всех значений перечисления:

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

    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...