Pregunta

En este código me sale un error de compilación, véase comentario:

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

  }

¿Hay una manera de hacer este trabajo, conseguir un valor de enumeración de una Map por uno de sus campos?

¿Fue útil?

Solución

El mapa es probablemente demasiado aquí. A menos que usted está planeando tener muchos más de cuatro valores de enumeración puede implementar getByTracName (String tn) simplemente interactuando sobre las cadenas válidas y devolviendo la correcta. Si las claves de los mapas son siempre los nombres de enumeración entonces usted puede hacer:

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

}

o puede hacerlo:

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

Otros consejos

Ja, divertido! Hace sólo unos días, me encontré con este.

A partir de la especificación del lenguaje Java, Tercera Edición, Sección 8.9:

Es un error de tiempo de compilación para hacer referencia a un campo estático de un tipo de enumeración que no es una constante de tiempo de compilación (§15.28) a partir de los constructores, ejemplo inicializador bloques, o variable de instancia inicializador expresiones de ese tipo. Es un error de tiempo de compilación para los constructores, ejemplo inicializador bloques, o variable de instancia inicializador expresiones de una constante de enumeración e para referirse a sí mismo o a una constante de enumeración del mismo tipo que se declara a la derecha de e.

discusión

Sin esta regla, al parecer código razonable podría fallar en tiempo de ejecución debido a la circularidad de inicialización inherentes a los tipos de enumeración. (Existe una circularidad en cualquier clase con un campo estático "auto-escrito".) Este es un ejemplo del tipo de código que fallaría:

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

inicialización estática de este tipo de enumeración sería lanzar una NullPointerException porque la variable estática mapa de colores no está inicializado cuando los constructores de las constantes de enumeración corren. La restricción anterior se asegure que tal código no se compilará.

Tenga en cuenta que el ejemplo puede ser fácilmente rediseñado para que funcione correctamente:

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

La versión refactorizado es claramente correcta, como inicialización estática se produce arriba a abajo.

Yo usaría el patrón reversible Enum :

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

cambiaría Type.java de la siguiente manera:

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

¿Qué hay de esto; no requiere que usted para hacer cambios en el código en dos lugares que es un poco propenso a errores 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;
    }

}

Mi propia solución, aunque requiere la repetición de todos los valores de enumeración:

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

    }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top