web-dev-qa-db-fra.com

Convertir d'énuméral en type enum

J'ai le type enum ReportTypeEnum qui est passé entre les méthodes de toutes mes classes mais je dois ensuite le transmettre à l'URL afin que j'utilise la méthode ordinale pour obtenir la valeur int. Une fois que je l'ai reçu dans mon autre page JSP, je dois le reconvertir en une variable ReportTypeEnum afin de pouvoir continuer à le transmettre. 

Comment puis-je convertir un ordinal en ReportTypeEnum?

Utilisation de Java 6 SE.

273
Lennie

Pour convertir un ordinal en représentation de son enum, procédez ainsi:

ReportTypeEnum value = ReportTypeEnum.values()[ordinal];

S'il vous plaît noter les limites du tableau. 

Notez que chaque appel à values() renvoie un tableau récemment cloné, ce qui peut avoir un impact négatif sur les performances. Vous voudrez peut-être mettre le tableau en cache s'il doit être appelé souvent. 

Exemple de code sur la façon de mettre en cache values() .


Cette réponse a été modifiée pour inclure les commentaires fournis dans les commentaires.

555
Joachim Sauer

C'est presque certainement une mauvaise idée . Bien sûr, si l'ordinal est de facto persistant (p. Ex. Parce que quelqu'un a marqué l'URL dans un signet), cela signifie que vous devez toujours conserver la commande enum à l'avenir, ce qui peut ne pas sembler évident pour les responsables du code.

Pourquoi ne pas encoder la enum en utilisant myEnumValue.name() (et le décoder via ReportTypeEnum.valueOf(s))?

135
oxbow_lakes

Si je vais utiliser values() beaucoup:

enum Suit {
   Hearts, Diamonds, Spades, Clubs;
   public static final Suit values[] = values();
}

En attendant partout.Java:

Suit suit = Suit.values[ordinal];

Faites attention aux limites de votre tableau.

83
QED

Vous pourriez utiliser une table de consultation statique:

public enum Suit {
  spades, hearts, diamonds, clubs;

  private static final Map<Integer, Suit> lookup = new HashMap<Integer, Suit>();

  static{
    int ordinal = 0;
    for (Suit suit : EnumSet.allOf(Suit.class)) {
      lookup.put(ordinal, suit);
      ordinal+= 1;
    }
  }

  public Suit fromOrdinal(int ordinal) {
    return lookup.get(ordinal);
  }
}
7
Jan

Je suis d'accord avec la plupart des gens pour dire que l'utilisation de l'ordinal est probablement une mauvaise idée. Je résous généralement ce problème en donnant à l'énumération un constructeur privé pouvant prendre par exemple une valeur de base de données, puis créer une fonction statique fromDbValue similaire à celle de la réponse de Jan.

public ReportTypeEnum {
    R1(1),
    R2(2),
    R3(3),
    R4(4),
    R5(5),
    R6(6),
    R7(7),
    R8(8);

    private static Logger log = LoggerFactory.getLogger(ReportEnumType.class);  
    private static Map<Integer, ReportTypeEnum> lookup;
    private Integer dbValue;

    private ReportTypeEnum(Integer dbValue) {
        this.dbValue = dbValue;
    }


    static {
        try {
            ReportTypeEnum[] vals = ReportTypeEnum.values();
            lookup = new HashMap<Integer, ReportTypeEnum>(vals.length);

            for (ReportTypeEnum  rpt: vals)
                lookup.put(rpt.getDbValue(), rpt);
         }
         catch (Exception e) {
             // Careful, if any exception is thrown out of a static block, the class
             // won't be initialized
             log.error("Unexpected exception initializing " + ReportTypeEnum.class, e);
         }
    }

    public static ReportTypeEnum fromDbValue(Integer dbValue) {
        return lookup.get(dbValue);
    }

    public Integer getDbValue() {
        return this.dbValue;
    }

}

Maintenant, vous pouvez changer l'ordre sans changer la recherche et vice versa.

6
jmkelm08

C'est ce que j'utilise. Je ne prétends pas que c'est beaucoup moins "efficace" que les solutions plus simples ci-dessus. Son objectif est de fournir un message d'exception beaucoup plus clair que "ArrayIndexOutOfBounds" lorsqu'une valeur ordinale non valide est utilisée dans la solution ci-dessus.

Il utilise le fait qu'EnumSet javadoc spécifie que l'itérateur renvoie les éléments dans leur ordre naturel. Il y a une affirmation si ce n'est pas correct.

Le test JUnit4 montre comment il est utilisé.

 /**
 * convert ordinal to Enum
 * @param clzz may not be null
 * @param ordinal
 * @return e with e.ordinal( ) == ordinal
 * @throws IllegalArgumentException if ordinal out of range
 */
public static <E extends Enum<E> > E lookupEnum(Class<E> clzz, int ordinal) {
    EnumSet<E> set = EnumSet.allOf(clzz);
    if (ordinal < set.size()) {
        Iterator<E> iter = set.iterator();
        for (int i = 0; i < ordinal; i++) {
            iter.next();
        }
        E rval = iter.next();
        assert(rval.ordinal() == ordinal);
        return rval;
    }
    throw new IllegalArgumentException("Invalid value " + ordinal + " for " + clzz.getName( ) + ", must be < " + set.size());
}

@Test
public void lookupTest( ) {
    Java.util.concurrent.TimeUnit tu = lookupEnum(TimeUnit.class, 3);
    System.out.println(tu);
}
4
gerardw
public enum Suit implements Java.io.Serializable, Comparable<Suit>{
  spades, hearts, diamonds, clubs;
  private static final Suit [] lookup  = Suit.values();
  public Suit fromOrdinal(int ordinal) {
    if(ordinal< 1 || ordinal> 3) return null;
    return lookup[value-1];
  }
}

la classe de test

public class MainTest {
    public static void main(String[] args) {
        Suit d3 = Suit.diamonds;
        Suit d3Test = Suit.fromOrdinal(2);
        if(d3.equals(d3Test)){
            System.out.println("Susses");
        }else System.out.println("Fails");
    }
}

J'apprécie que vous partagiez avec nous si vous avez un code plus efficace. Mon enum est énorme et appelé des milliers de fois.

0
Ar maj

C'est ce que je fais sur Android avec Proguard:

public enum SomeStatus {
    UNINITIALIZED, STATUS_1, RESERVED_1, STATUS_2, RESERVED_2, STATUS_3;//do not change order

    private static SomeStatus[] values = null;
    public static SomeStatus fromInteger(int i) {
        if(SomeStatus.values == null) {
            SomeStatus.values = SomeStatus.values();
        }
        if (i < 0) return SomeStatus.values[0];
        if (i >= SomeStatus.values.length) return SomeStatus.values[0];
        return SomeStatus.values[i];
    }
}

c'est court et je n'ai pas besoin de m'inquiéter d'avoir une exception dans Proguard

0
Someone Somewhere

Donc, un moyen est de faire ExampleEnum valueOfOrdinal = ExampleEnum.values()[ordinal]; qui fonctionne et qui est facile, cependant, comme mentionné précédemment, ExampleEnum.values() renvoie un nouveau tableau cloné pour chaque appel. Cela peut être inutilement coûteux. Nous pouvons résoudre ce problème en mettant en cache le tableau, comme ceci: ExampleEnum[] values = values(). Il est également "dangereux" de permettre à notre tableau mis en cache d'être modifié. Quelqu'un pourrait écrire ExampleEnum.values[0] = ExampleEnum.type2; Je le rendrais donc privé avec une méthode d'accès ne faisant pas de copie supplémentaire.

private enum ExampleEnum{
    type0, type1, type2, type3;
    private static final ExampleEnum[] values = values();
    public static ExampleEnum value(int ord) {
        return values[ord];
    }
}

Vous utiliseriez ExampleEnum.value(ordinal) pour obtenir la valeur enum associée à ordinal

0
joe pelletier

Vous pouvez définir une méthode simple comme:

public enum Alphabet{
    A,B,C,D;

    public static Alphabet get(int index){
        return Alphabet.values()[index];
    }
}

Et utilisez-le comme:

System.out.println(Alphabet.get(2));
0
Amir Forsati