2010-11-25 29 views
2

Dans ce code, je reçois une erreur du compilateur, voir le commentaire:ne peut pas accéder champ statique dans ENUM initialiseur

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

    } 

est-il un moyen de faire ce travail, obtenir une valeur ENUM d'un Map par l'un de ses champs ?

+0

Il peut être utile de préciser si la 'clé' pour la carte est toujours le 'nom' de l'énumération. Si c'est le cas, cela fait une grande différence dans la façon dont vous feriez cela. – DJClayworth

Répondre

9

La carte est probablement ici surpuissant. Sauf si vous prévoyez d'avoir plus de quatre valeurs enum, vous pouvez implémenter getByTracName (String tn) en itérant simplement sur les chaînes valides et en renvoyant la bonne. Si les clés de carte sont toujours les noms de ENUM, vous pouvez le faire:

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

}

ou vous pouvez faire:

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

'Enum. valueOf (Type.class, tn) 'devrait être écrit comme' Type.valueOftn) ' – user102008

0

Ma propre solution, mais il nécessite de répéter toutes les valeurs ENUM:

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

    } 
+0

En ajoutant un peu plus de code, vous pouvez réduire la répétition du code comme mentionné ici: http://stackoverflow.com/questions/4278432/cannot-access-static-field-within-enum-initialiser/4278931#4278931 –

2

j'utiliser le 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); 
    } 
} 

Vous changeriez Type.java comme suit:

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

Je dois dire que je suis un peu dépassé. :-) –

+0

Oui, mais c'est réutilisable :) – mtpettyp

1

Comment à ce sujet; ne nécessite pas de faire des changements de code à deux endroits qui est une sorte d'erreur sujettes 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; 
    } 

} 
7

Hah, drôle! Il y a quelques jours, j'ai trébuché sur ça.

De la spécification du langage Java, troisième édition, Section 8.9:

Il est une erreur de compilation pour faire référence à un champ statique d'un type ENUM qui n'est pas une constante de compilation (§15.28) à partir de constructeurs, de blocs d'initialisation d'instance ou d'expressions d'initialisation de variable d'instance de ce type. Il s'agit d'une erreur de compilation pour les constructeurs, les blocs d'initialisation d'instance ou les expressions d'initialisation de variable d'instance d'une constante enum e pour se référer à elle ou à une constante enum du même type déclarée à droite de e.

Discussion

Sans cette règle, le code semble raisonnable échouer au moment de l'exécution en raison de l'initialisation tautologique types enum. (A circularité existe dans une classe avec un « auto typé » champ statique.) Voici un exemple du genre de code qui échouerait:

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

initialisation statique de ce type ENUM une NullPointerException car la variable statique colorMap n'est pas initialisée lorsque les constructeurs des constantes enum s'exécutent. La restriction ci-dessus garantit que ce code ne compilera pas.

Notez que l'exemple peut facilement être refactorisé pour fonctionner correctement:

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 version remaniée est clairement correcte, que l'initialisation statique se produit haut en bas.