Comment obtenir une valeur énumérée à partir d'une valeur de chaîne en Java?

1984

Dis que j'ai une énumération qui est juste

public enum Blah {
    A, B, C, D
}

et je voudrais trouver la valeur énumérée d'une chaîne, par exemple "A"qui serait Blah.A. Comment serait-il possible de faire cela?

Est-ce Enum.valueOf()la méthode dont j'ai besoin? Si oui, comment pourrais-je l'utiliser?

Malachie
la source

Réponses:

2259

Oui, Blah.valueOf("A")vous donnera Blah.A.

Notez que le nom doit être une correspondance exacte , y compris la casse: Blah.valueOf("a")et les Blah.valueOf("A ")deux jettent un IllegalArgumentException.

Les méthodes statiques valueOf()et values()sont créées au moment de la compilation et n'apparaissent pas dans le code source. Ils apparaissent cependant dans Javadoc; par exemple, Dialog.ModalityTypemontre les deux méthodes.

Michael Myers
la source
100
Pour référence, la Blah.valueOf("A")méthode est sensible à la casse et ne tolère pas les espaces étrangers, d'où la solution alternative proposée ci-dessous par @ JoséMi.
Brett
3
@Michael Myers, Étant donné que cette réponse est de loin la plus votée, dois-je comprendre que c'est une bonne pratique de définir une énumération et sa valeur de chaîne pour qu'elles soient exactement les mêmes?
Kevin Meredith
4
@KevinMeredith: Si vous voulez dire la toString()valeur, non, je ne dirais pas cela. name()vous obtiendra le nom réel défini de la constante enum à moins que vous ne le remplaciez.
Michael Myers
3
Qu'entendez-vous exactement par «sont créés au moment de la compilation et n'apparaissent pas dans le code source». ?
treesAreEverywhere
8
@treesAreEverywhere Plus précisément, ces méthodes sont générées (ou synthétisées ) par le compilateur. La enum Blah {...}définition actuelle ne doit pas essayer de déclarer son propre valuesni valuesOf. C'est comme comment vous pouvez écrire "AnyTypeName.class" même si vous n'avez jamais réellement déclaré une variable membre "class"; le compilateur fait tout juste fonctionner. (Cette réponse peut ne plus vous être utile 3 mois plus tard, mais juste au cas où.)
Ti Strga
864

Une autre solution si le texte n'est pas le même que la valeur d'énumération:

public enum Blah {
    A("text1"),
    B("text2"),
    C("text3"),
    D("text4");

    private String text;

    Blah(String text) {
        this.text = text;
    }

    public String getText() {
        return this.text;
    }

    public static Blah fromString(String text) {
        for (Blah b : Blah.values()) {
            if (b.text.equalsIgnoreCase(text)) {
                return b;
            }
        }
        return null;
    }
}
JoséMi
la source
396
throw new IllegalArgumentException("No constant with text " + text + " found")serait mieux que return null.
whiskeysierra
8
@whiskeysierra Jon Skeet ne serait pas d'accord avec cela. stackoverflow.com/questions/1167982/…
Sanghyun Lee
11
@Sangdol Pourriez- vous nous expliquer pourquoi le retour de null est préférable?
whiskeysierra
57
@Sangdol généralement c'est une bonne chose de vérifier ce que SUN - oups - Oracle fait dans la même situation. Et comme Enum.valueOf () le montre, il est préférable de lever une exception dans ce cas. Parce que c'est une situation exceptionnelle. "Optimisation des performances" est une mauvaise excuse pour écrire du code illisible ;-)
raudi
5
Eh bien, vous pouvez également utiliser l'annotation @Nullable pour la rendre "lisible" ;-)
JoséMi
121

Voici un utilitaire astucieux que j'utilise:

/**
 * A common method for all enums since they can't have another base class
 * @param <T> Enum type
 * @param c enum type. All enums must be all caps.
 * @param string case insensitive
 * @return corresponding enum, or null
 */
public static <T extends Enum<T>> T getEnumFromString(Class<T> c, String string) {
    if( c != null && string != null ) {
        try {
            return Enum.valueOf(c, string.trim().toUpperCase());
        } catch(IllegalArgumentException ex) {
        }
    }
    return null;
}

Ensuite, dans ma classe d'énumération, j'ai généralement ceci pour économiser de la frappe:

public static MyEnum fromString(String name) {
    return getEnumFromString(MyEnum.class, name);
}

Si vos énumérations ne sont pas toutes des majuscules, modifiez simplement le Enum.valueOf ligne.

Dommage que je ne peux pas utiliser T.classpour Enum.valueOfque Test effacé.

Geoffrey Zheng
la source
176
Ce bloc de capture vide me rend vraiment fou, désolé.
whiskeysierra
32
@LazloBonin: Les exceptions concernent des conditions exceptionnelles, pas le contrôle du flux. Procurez-vous une copie de Effective Java .
Rétablir Monica - M. Schröder
10
Si l'API Java que vous souhaitez utiliser lève une exception et que vous ne voulez pas que votre code en génère une, vous pouvez soit avaler l'exception comme ceci, soit réécrire la logique à partir de zéro afin qu'aucune exception ne soit levée en premier lieu. Avaler l'exception est souvent le moindre mal.
Nate CK
47
Horrible! Toujours, toujours intercepter les exceptions où vous pouvez les gérer. L'exemple ci-dessus est un exemple parfait de comment NE PAS le faire . Pourquoi? Il renvoie donc NULL, et l'appelant doit alors vérifier par rapport à NULL ou lancer un NPE. Si l'appelant sait comment gérer la situation, faire un if vs try-catch peut sembler un peu plus élégant, MAIS s'il ne peut pas gérer, il doit à nouveau passer null et l'appelant de l'appelant doit à nouveau vérifier contre NULL , etc. etc.
raudi
10
Pour être juste envers la solution ci-dessus, il existe vraiment des cas d'utilisation vous obligeant à retourner null au lieu de lancer IllegalArgumentException et à interrompre le flux de votre programme, par exemple, en mappant les énumérations entre un schéma de service Web et un schéma de base de données où ils ne sont pas toujours un -à une. Cependant, je suis d'accord que le bloc catch ne doit jamais être laissé vide. Mettez du code comme log.warn ou quelque chose à des fins de suivi.
Adrian M
102

Utilisez le modèle de Joshua Bloch, Java efficace :

(simplifié par souci de concision)

enum MyEnum {
    ENUM_1("A"),
    ENUM_2("B");

    private String name;

    private static final Map<String,MyEnum> ENUM_MAP;

    MyEnum (String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }

    // Build an immutable map of String name to enum pairs.
    // Any Map impl can be used.

    static {
        Map<String,MyEnum> map = new ConcurrentHashMap<String, MyEnum>();
        for (MyEnum instance : MyEnum.values()) {
            map.put(instance.getName(),instance);
        }
        ENUM_MAP = Collections.unmodifiableMap(map);
    }

    public static MyEnum get (String name) {
        return ENUM_MAP.get(name);
    }
}

Regarde aussi:

Exemple Oracle Java utilisant Enum et Map of instances

Ordre d'exécution des blocs statiques dans un type Enum

Comment puis-je rechercher une énumération Java à partir de sa valeur de chaîne

Darrell Teague
la source
4
si Joshua Bloch l'a dit, c'est la seule façon de procéder :-). C'est dommage que je doive toujours faire défiler ici.
dermoritz
11
C'est encore plus simple en Java 8 comme vous pouvez le faire: Stream.of(MyEnum.values()).collect(toMap(Enum::name, identity()))je recommande également de remplacer toString () (transmis via le constructeur) et de l'utiliser à la place du nom, surtout si l'énumération est associée à des données sérialisables car cela vous permet de contrôler le boîtier sans donner Sonar un ajustement.
Novaterata
1
Java 8 peut / peut certainement changer beaucoup de (meilleures) réponses sur ce forum. Je ne sais pas si jamais la queue (sonar) remue le chien (code d'application).
Darrell Teague le
3
Si vous allez le mettre de unmodifiableMaptoute façon, il n'y a aucun avantage à commencer par un ConcurrentHashMap. Utilisez simplement un HashMap. (Si vous avez de la goyave, ImmutableMapje le recommanderais à la place!)
Daniel Pryden
9
L'initialisation statique est intrinsèquement synchronisée , il n'y a donc absolument aucune raison de l'utiliser ConcurrentHashMapici, où la carte n'est jamais modifiée après l'initialisation. Par conséquent, même par exemple, l'exemple dans le JLS lui-même utilise un régulier HashMap.
Radiodef
74

Vous devez également être prudent avec votre cas. Permettez-moi de vous expliquer: faire des Blah.valueOf("A")travaux, mais Blah.valueOf("a")ne fonctionnera pas. Là encore, ça Blah.valueOf("a".toUpperCase(Locale.ENGLISH))marcherait.

modifier
Changé toUpperCaseen toUpperCase(Locale.ENGLISH)basé sur tc. commentaire et les documents java

edit2 Sur android, vous devriez utiliser Locale.US, comme le souligne sulai .

João Portela
la source
6
Méfiez-vous des paramètres régionaux par défaut!
tc.
3
Pour vous utilisateurs Android, je voudrais souligner que la documentation Android encourage explicitement l'utilisation d' Locale.USentrées / sorties lisibles par machine.
sulai
2
@Trengot Oui, malheureusement. La Turquie en est un bon exemple. Combinez cela avec la gestion cassée de Java des jeux de caractères par défaut (par défaut Latin sur Windows au lieu d'Unicode) et vous constaterez qu'il est presque toujours dangereux d'utiliser les versions par défaut des méthodes qui acceptent un jeu de caractères ou des paramètres régionaux. Vous devez presque toujours les définir explicitement.
Stijn de Witt
Je ne suis pas sûr que les jeux de caractères "par défaut" et autres de Java soient "cassés" en soi mais accordés, la valeur par défaut UTF-8 au lieu des remplacements (ce qui devrait toujours être fait pour être explicite) aurait fait de meilleurs systèmes pour les programmeurs juniors qui ne comprennent généralement pas concepts charset.
Darrell Teague
38

Voici une méthode qui peut le faire pour n'importe quel Enum et ne respecte pas la casse.

/** 
 * Finds the value of the given enumeration by name, case-insensitive. 
 * Throws an IllegalArgumentException if no match is found.  
 **/
public static <T extends Enum<T>> T valueOfIgnoreCase(
        Class<T> enumeration, String name) {

    for (T enumValue : enumeration.getEnumConstants()) {
        if (enumValue.name().equalsIgnoreCase(name)) {
            return enumValue;
        }
    }

    throw new IllegalArgumentException(String.format(
        "There is no value with name '%s' in Enum %s",
        name, enumeration.getName()
    ));
}
Patrick Arnesen
la source
Cette variation le fait correctement: equalsIgnoreCasec'est la voie à suivre. +1
Stijn de Witt
Comme l'insensibilité à la casse, mais ... préfère les énumérations aux affectations de chaînes (aléatoires) pour les touches et ... mineur mais l'itération est moins performante pour une recherche aussi répétitive. D'où impl de EnumMap et al.
Darrell Teague
Cela ne fonctionne pas ! J'ai changé le equalsIgnoreCase en égal à pour mon but. Le code a échoué même si les deux entrées égales étaient exactement les mêmes.
MasterJoe2
36

L'utilisation Blah.valueOf(string)est la meilleure, mais vous pouvez également l'utiliser Enum.valueOf(Blah.class, string).

Peter Lawrey
la source
1
Sensible à la casse, n'aide pas!
Murtaza Kanchwala
@MurtazaKanchwala Pouvez-vous clarifier votre commentaire? Qu'essayez-vous de faire?
Peter Lawrey
2
Salut @PeterLawrey, J'essayais de récupérer un Enum à partir d'un String public enum ObjectType {PERSON ("Person") public String parameterName; ObjectType (String parameterName) {this.parameterName = parameterName; } public String getParameterName () {return this.parameterName; } ObjectType statique public fromString (String parameterName) {if (parameterName! = null) {for (ObjectType objType: ObjectType.values ​​()) {if (parameterName.equalsIgnoreCase (objType.parameterName)) {return objType; }}} return null; }}
Murtaza Kanchwala
35

Dans Java 8 ou version ultérieure, à l'aide de Streams :

public enum Blah
{
    A("text1"),
    B("text2"),
    C("text3"),
    D("text4");

    private String text;

    Blah(String text) {
        this.text = text;
    }

    public String getText() {
        return this.text;
    }

    public static Optional<Blah> fromText(String text) {
        return Arrays.stream(values())
          .filter(bl -> bl.text.equalsIgnoreCase(text))
          .findFirst();
    }
}
Hans Schreuder
la source
Pas sûr que ce soit en quelque sorte une meilleure réponse. Dans ce cas, les flux sont un itérateur à un seul thread comme les autres sur toutes les valeurs et sans doute moins performant qu'un implément de recherche de carte. Les flux ont plus de valeur dans un contexte multithread où, par exemple, l'exécution parallèle d'un fichier texte séparé par des sauts de ligne peut améliorer les performances.
Darrell Teague le
28

Si vous ne voulez pas écrire votre propre utilitaire, utilisez Google bibliothèque:

Enums.getIfPresent(Blah.class, "A")

Contrairement à la fonction java intégrée, il vous permet de vérifier si A est présent dans Blah et ne lève pas d'exception.

Andrejs
la source
7
la partie triste est que cela renvoie un google en option et non en java en option
javaProgrammer
Vrai. Exofecté cependant. Google et Netflix ont d'excellentes bibliothèques Java. Lorsqu'il y a chevauchement avec des classes de rattrapage Java implémentées dans des versions plus récentes, cela cause inévitablement des problèmes. En quelque sorte, vous devez tout faire sur une seule librairie de fournisseurs.
Darrell Teague du
26

Mes 2 cents ici: utiliser Java8 Streams + vérifier une chaîne exacte:

public enum MyEnum {
    VALUE_1("Super"),
    VALUE_2("Rainbow"),
    VALUE_3("Dash"),
    VALUE_3("Rocks");

    private final String value;

    MyEnum(String value) {
        this.value = value;
    }

    /**
     * @return the Enum representation for the given string.
     * @throws IllegalArgumentException if unknown string.
     */
    public static MyEnum fromString(String s) throws IllegalArgumentException {
        return Arrays.stream(MyEnum.values())
                .filter(v -> v.value.equals(s))
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException("unknown value: " + s));
    }
}

** ÉDITER **

Renommée la fonction pour la fromString()nommer en utilisant cette convention, vous obtiendrez certains avantages du langage Java lui-même; par exemple:

  1. Conversion directe des types à l'annotation HeaderParam
Manu
la source
1
Alternativement, pour vous permettre d'écrire des switchblocs plus lisibles , vous pouvez le faire à la .orElse(null)place de .orElseThrow()afin de pouvoir coder la levée d'exception dans la defaultclause - et inclure des informations plus utiles si nécessaire. Et pour le rendre plus clément, vous pouvez utiliserv -> Objects.equals(v.name, s == null ? "" : s.trim().toUpperCase())
Adam
ou simplement renvoyer le Optionalfrom findFirst(), permettant à l'utilisateur de décider s'il veut .orElse(null), orElseThrow()ou quoi que ce soit ....
user85421
1
La déclaration d'un public static MyEnum valueOf(String)est en fait une erreur de compilation, car elle est la même que celle définie implicitement, donc l'ancienne version de votre réponse est en fait meilleure. ( jls , ideone )
Radiodef
Dans mon option, il vaut mieux éviter les exceptions et utiliser facultatif. En plus de cela, nous devons annuler null et utiliser plutôt Optional à la place.
Hans Schreuder
Encore une fois, n'oubliez pas que même si le code est moins ou meilleur ... une implémentation Stream comme celle-ci n'est qu'un itérateur sur toutes les valeurs par rapport à une recherche de carte (moins performante).
Darrell Teague le
16

Vous devrez peut-être:

public enum ObjectType {
    PERSON("Person");

    public String parameterName;

    ObjectType(String parameterName) {
        this.parameterName = parameterName;
    }

    public String getParameterName() {
        return this.parameterName;
    }

    //From String method will return you the Enum for the provided input string
    public static ObjectType fromString(String parameterName) {
        if (parameterName != null) {
            for (ObjectType objType : ObjectType.values()) {
                if (parameterName.equalsIgnoreCase(objType.parameterName)) {
                    return objType;
                }
            }
        }
        return null;
    }
}

Encore un ajout:

   public static String fromEnumName(String parameterName) {
        if (parameterName != null) {
            for (DQJ objType : DQJ.values()) {
                if (parameterName.equalsIgnoreCase(objType.name())) {
                    return objType.parameterName;
                }
            }
        }
        return null;
    }

Cela vous renverra la valeur par un nom d'énumération chaîne. Par exemple, si vous fournissez "PERSON" dans le fromEnumName, cela vous renverra la valeur d'énum ie "Personne"

Murtaza Kanchwala
la source
13

Une autre façon de le faire en utilisant la méthode statique implicite name()d'Enum. nom renverra la chaîne exacte utilisée pour créer cette énumération qui peut être utilisée pour vérifier la chaîne fournie:

public enum Blah {

    A, B, C, D;

    public static Blah getEnum(String s){
        if(A.name().equals(s)){
            return A;
        }else if(B.name().equals(s)){
            return B;
        }else if(C.name().equals(s)){
            return C;
        }else if (D.name().equals(s)){
            return D;
        }
        throw new IllegalArgumentException("No Enum specified for this string");
    }
}

Essai:

System.out.println(Blah.getEnum("B").name());

//it will print B  B

inspiration: 10 exemples d'Enum en Java

Vikram
la source
7
C'est essentiellement ce qui valueOffait pour vous. Cette méthode statique n'offre rien de plus, exception et tout. Les constructions if / else sont alors très dangereuses ... toute nouvelle constante enum ajoutée entraînera la rupture de cette méthode sans changement.
YoYo
Considérez également cet exemple de la façon dont nous pouvons utiliser valueOf pour effectuer une recherche insensible à la casse, ou comment nous pouvons éviter son exception et utiliser des alias pour fournir des noms alternatifs: stackoverflow.com/a/12659023/744133
YoYo
2
name()n'est pas statique.
nrubin29
10

Solution utilisant les bibliothèques Guava. La méthode getPlanet () n'est pas sensible à la casse, donc getPlanet ("MerCUrY") renverra Planet.MERCURY.

package com.universe.solarsystem.planets;
import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Enums;
import com.google.common.base.Optional;

//Pluto and Eris are dwarf planets, who cares!
public enum Planet {
   MERCURY,
   VENUS,
   EARTH,
   MARS,
   JUPITER,
   SATURN,
   URANUS,
   NEPTUNE;

   public static Planet getPlanet(String name) {
      String val = StringUtils.trimToEmpty(name).toUpperCase();
      Optional <Planet> possible = Enums.getIfPresent(Planet.class, val);
      if (!possible.isPresent()) {
         throw new IllegalArgumentException(val + "? There is no such planet!");
      }
      return possible.get();
   }
}
javabrew
la source
8

Pour compléter les réponses précédentes et répondre à certaines des discussions sur les valeurs NULL et NPE, j'utilise les options de la goyave pour gérer les cas absents / non valides. Cela fonctionne très bien pour l'analyse d'URI / paramètres.

public enum E {
    A,B,C;
    public static Optional<E> fromString(String s) {
        try {
            return Optional.of(E.valueOf(s.toUpperCase()));
        } catch (IllegalArgumentException|NullPointerException e) {
            return Optional.absent();
        }
    }
}

Pour ceux qui ne le savent pas, voici quelques informations supplémentaires sur la façon d'éviter le null avec Facultatif: https://code.google.com/p/guava-libraries/wiki/UsingAndAvoidingNullExplained#Optional

à M
la source
C'est une très bonne réponse pour les modèles associés et l'utilisation de Facultatif dans une énumération - en tirant parti du fait que les énumérations sont également des classes et peuvent donc être décorées avec des méthodes, des méthodes de substitution, etc. les valeurs nulles retournées par les méthodes rendent cette construction boguée (NPE à des endroits inconnus dans les chaînes Fluent d'appels de méthode).
Darrell Teague le
8

Dans Java 8, le modèle de carte statique est encore plus facile et est ma méthode préférée. Si vous souhaitez utiliser l'énumération avec Jackson, vous pouvez remplacer toString et l'utiliser à la place de nom, puis annoter avec@JsonValue

public enum MyEnum {
    BAR,
    BAZ;
    private static final Map<String, MyEnum> MAP = Stream.of(MyEnum.values()).collect(Collectors.toMap(Enum::name, Function.identity()));
    public static MyEnum fromName(String name){
        return MAP.get(name);
    }
}

public enum MyEnumForJson {
    BAR("bar"),
    BAZ("baz");
    private static final Map<String, MyEnumForJson> MAP = Stream.of(MyEnumForJson.values()).collect(Collectors.toMap(Object::toString, Function.identity()));
    private final String value;

    MyEnumForJson(String value) {
        this.value = value;
    }

    @JsonValue
    @Override
    public String toString() {
        return value;
    }

    public static MyEnumForJson fromValue(String value){
        return MAP.get(value);
    }
}
Novaterata
la source
Jackson est une implémentation JSON (JavaScript Object Notation). La question d'origine n'avait rien à voir avec JSON.
Darrell Teague le
La partie JSON n'était que des éléments bonus que je trouvais pertinents à l'époque, car obtenir un Enum à partir d'une chaîne est essentiellement un type de désérialisation et JSON / Jackson est probablement la solution de sérialisation la plus populaire.
Novaterata
Comprenez, mais du point de vue de la modération - cela n'a pas contribué à répondre à la question de l'OP, donc essayez simplement de définir le contexte là-bas.JSON est en effet la voie à suivre pour convertir des objets sous forme canonique en Java, Jackson étant une excellente bibliothèque.
Darrell Teague
6
public static MyEnum getFromValue(String value) {
    MyEnum resp = null;
    MyEnum nodes[] = values();
    for(int i = 0; i < nodes.length; i++) {
        if(nodes[i].value.equals(value)) {
            resp = nodes[i];
            break;
        }
    }
    return resp;
}
Prasobh.Kollattu
la source
jetez un oeil à ce lien pour des guides sur la façon de répondre et de poser des questions sur stackoverflow.com: stackoverflow.com/faq
bakoyaro
1
C'est plus ou moins la même réponse que JoséMi
Rup
6

Méthode O (1) inspirée du code généré par l'épargne qui utilise une table de hachage.

public enum USER {
        STUDENT("jon",0),TEACHER("tom",1);

        private static final Map<String, Integer> map = new HashMap<>();

        static {
                for (USER user : EnumSet.allOf(USER.class)) {
                        map.put(user.getTypeName(), user.getIndex());
                }
        }

        public static int findIndexByTypeName(String typeName) {
                return map.get(typeName);
        }

        private USER(String typeName,int index){
                this.typeName = typeName;
                this.index = index;
        }
        private String typeName;
        private int index;
        public String getTypeName() {
                return typeName;
        }
        public void setTypeName(String typeName) {
                this.typeName = typeName;
        }
        public int getIndex() {
                return index;
        }
        public void setIndex(int index) {
                this.index = index;
        }

}
Sisyphe
la source
Notez que les identificateurs zéro (0) et un (1) ne sont pas nécessaires. La méthode Enum values ​​() renvoie les membres dans le même ordre que codé. Ainsi, la première entrée sera le zéro ordinal, la seconde, etc.
Darrell Teague
6

Enum est très utile, j'ai beaucoup utilisé Enumpour ajouter une description de certains champs dans différentes langues, comme l'exemple suivant:

public enum Status {

    ACT(new String[] { "Accepted", "مقبول" }),
    REJ(new String[] { "Rejected", "مرفوض" }),
    PND(new String[] { "Pending", "في الانتظار" }),
    ERR(new String[] { "Error", "خطأ" }),
    SNT(new String[] { "Sent", "أرسلت" });

    private String[] status;

    public String getDescription(String lang) {
        return lang.equals("en") ? status[0] : status[1];
    }

    Status(String[] status) {
        this.status = status;
    }
}

Et puis vous pouvez récupérer la description dynamiquement en fonction du code de langue transmis à la getDescription(String lang)méthode, par exemple:

String statusDescription = Status.valueOf("ACT").getDescription("en");
Ebraheem Alrabee »
la source
1
Bon exemple en poussant Enums plus loin. Aurait fait le codage de la langue en utilisant les noms statiques standard et la recherche dans une carte, mais quand même ... un bon exemple d'avoir une énumération avec des balises différentes pour ce qui est essentiellement la même valeur logique.
Darrell Teague le
5

Qu'en est-il de?

public enum MyEnum {
    FIRST,
    SECOND,
    THIRD;

    public static Optional<MyEnum> fromString(String value){
        try{
            return Optional.of(MyEnum.valueOf(value));
        }catch(Exception e){
            return Optional.empty();
        }
    }
}
DCO
la source
4

java.lang.Enum définit plusieurs méthodes utiles, disponibles pour tous les types d'énumération en Java:

  • Vous pouvez utiliser name() méthode pour obtenir le nom des constantes Enum. Le littéral de chaîne utilisé pour écrire les constantes enum est leur nom.
  • De même values() méthode peut être utilisée pour obtenir un tableau de toutes les constantes Enum d'un type Enum.
  • Et pour la question posée, vous pouvez utiliser une valueOf()méthode pour convertir n'importe quelle constante String en Enum en Java, comme indiqué ci-dessous.
public class EnumDemo06 {
    public static void main(String args[]) {
        Gender fromString = Gender.valueOf("MALE");
        System.out.println("Gender.MALE.name() : " + fromString.name());
    }

    private enum Gender {
        MALE, FEMALE;
    }
}

Output:
Gender.MALE.name() : MALE

Dans cet extrait de code, la valueOf()méthode retourne une constante Enum Gender.MALE, appelant le nom sur ce retour "MALE".

KNU
la source
4

La bibliothèque commons-lang d' Apache a une fonction statique org.apache.commons.lang3.EnumUtils.getEnum qui mappera une chaîne à votre type Enum. Même réponse que Geoffreys, mais pourquoi lancer la vôtre quand elle est déjà dans la nature.

pjklauser
la source
1
Bon commentaire (SEC) mais ... alors que la plupart des trucs Apache Commons sont géniaux, j'ai moi-même trouvé plusieurs bugs et anti-patterns dans cette base. Ainsi, faire référence à la mise en œuvre de Joshua Bloch pourrait être sur une base plus solide. Se résume alors à devoir revoir le code Apache pour savoir ce que quelqu'un a implémenté. Si c'était le fameux Doug Leah qui a réécrit la concurrence Java ... alors je lui ferais implicitement confiance.
Darrell Teague le
4

Ajout à la réponse la mieux notée, avec un utilitaire utile ...

valueOf() lève deux exceptions différentes dans les cas où il n'aime pas son entrée.

  • IllegalArgumentException
  • NullPointerExeption

Si vos exigences sont telles que vous n'avez aucune garantie que votre chaîne correspondra définitivement à une valeur d'énumération, par exemple si les données de chaîne proviennent d'une base de données et peuvent contenir une ancienne version de l'énumération, vous devrez les gérer souvent...

Voici donc une méthode réutilisable que j'ai écrite qui nous permet de définir une énumération par défaut à renvoyer si la chaîne que nous transmettons ne correspond pas.

private static <T extends Enum<T>> T valueOf( String name , T defaultVal) {
        try {
            return Enum.valueOf(defaultVal.getDeclaringClass() , name);
        } catch (IllegalArgumentException | NullPointerException e) {
            return defaultVal;
        }
    }

Utilisez-le comme ceci:

public enum MYTHINGS {
    THINGONE,
    THINGTWO
}

public static void main(String [] asd) {
  valueOf("THINGTWO" , MYTHINGS.THINGONE);//returns MYTHINGS.THINGTWO
  valueOf("THINGZERO" , MYTHINGS.THINGONE);//returns MYTHINGS.THINGONE
}
lance.dolan
la source
2

Comme une switchversion-n'a pas encore été mentionnée, je l'introduis (en réutilisant l'énumération OP):

  private enum Blah {
    A, B, C, D;

    public static Blah byName(String name) {
      switch (name) {
        case "A":
          return A;
        case "B":
          return B;
        case "C":
          return C;
        case "D":
          return D;
        default:
          throw new IllegalArgumentException(
            "No enum constant " + Blah.class.getCanonicalName() + "." + name);
      }
    }
  }

Comme cela ne donne aucune valeur supplémentaire à la valueOf(String name)méthode, il est logique de définir une méthode supplémentaire si nous voulons avoir un comportement différent. Si nous ne voulons pas augmenter un, IllegalArgumentExceptionnous pouvons changer l'implémentation en:

  private enum Blah {
    A, B, C, D;

    public static Blah valueOfOrDefault(String name, Blah defaultValue) {
      switch (name) {
        case "A":
          return A;
        case "B":
          return B;
        case "C":
          return C;
        case "D":
          return D;
        default:
          if (defaultValue == null) {
            throw new NullPointerException();
          }
          return defaultValue;
      }
    }
  }

En fournissant une valeur par défaut, nous conservons le contrat de Enum.valueOf(String name)sans en lancer un IllegalArgumentException de cette manière qui en aucun cas nulln'est retourné. Par conséquent, nous lançons un NullPointerExceptionsi le nom est nullet en cas de defaultsi defaultValueest null. Voilà comment ça valueOfOrDefaultmarche.

Cette approche adopte la conception de Map-Interface qui fournit une méthode à Map.getOrDefault(Object key, V defaultValue)partir de Java 8.

LuCio
la source
1

Un autre utilitaire capturant en sens inverse. Utiliser une valeur qui identifie cet Enum, pas à partir de son nom.

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.EnumSet;

public class EnumUtil {

    /**
     * Returns the <code>Enum</code> of type <code>enumType</code> whose a 
     * public method return value of this Enum is 
     * equal to <code>valor</code>.<br/>
     * Such method should be unique public, not final and static method 
     * declared in Enum.
     * In case of more than one method in match those conditions
     * its first one will be chosen.
     * 
     * @param enumType
     * @param value
     * @return 
     */
    public static <E extends Enum<E>> E from(Class<E> enumType, Object value) {
        String methodName = getMethodIdentifier(enumType);
        return from(enumType, value, methodName);
    }

    /**
     * Returns the <code>Enum</code> of type <code>enumType</code> whose  
     * public method <code>methodName</code> return is 
     * equal to <code>value</code>.<br/>
     *
     * @param enumType
     * @param value
     * @param methodName
     * @return
     */
    public static <E extends Enum<E>> E from(Class<E> enumType, Object value, String methodName) {
        EnumSet<E> enumSet = EnumSet.allOf(enumType);
        for (E en : enumSet) {
            try {
                String invoke = enumType.getMethod(methodName).invoke(en).toString();
                if (invoke.equals(value.toString())) {
                    return en;
                }
            } catch (Exception e) {
                return null;
            }
        }
        return null;
    }

    private static String getMethodIdentifier(Class<?> enumType) {
        Method[] methods = enumType.getDeclaredMethods();
        String name = null;
        for (Method method : methods) {
            int mod = method.getModifiers();
            if (Modifier.isPublic(mod) && !Modifier.isStatic(mod) && !Modifier.isFinal(mod)) {
                name = method.getName();
                break;
            }
        }
        return name;
    }
}

Exemple:

public enum Foo {
    ONE("eins"), TWO("zwei"), THREE("drei");

    private String value;

    private Foo(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }
}

EnumUtil.from(Foo.class, "drei")renvoie Foo.THREE, car il utilisera getValuepour faire correspondre "drei", qui est une méthode publique unique, non finale et non statique dans Foo. Dans le cas Foo a plus que la méthode publique, pas définitive et non statique, par exemple, getTranslatequi renvoie « drei », l'autre méthode peut être utilisée: EnumUtil.from(Foo.class, "drei", "getTranslate").

Moesio
la source
1

Kotlin Solution

Créez un poste, puis appelez valueOf<MyEnum>("value"). Si le type n'est pas valide, vous obtiendrez null et devrez le gérer

inline fun <reified T : Enum<T>> valueOf(type: String): T? {
    return try {
        java.lang.Enum.valueOf(T::class.java, type)
    } catch (e: Exception) {
        null
    }
}

Alternativement, vous pouvez définir une valeur par défaut, appeler valueOf<MyEnum>("value", MyEnum.FALLBACK)et éviter une réponse nulle. Vous pouvez étendre votre énumération spécifique pour que la valeur par défaut soit automatique

inline fun <reified T : Enum<T>> valueOf(type: String, default: T): T {
    return try {
        java.lang.Enum.valueOf(T::class.java, type)
    } catch (e: Exception) {
        default
    }
}

Ou si vous voulez les deux, faites le second:

inline fun <reified T : Enum<T>> valueOf(type: String, default: T): T = valueOf<T>(type) ?: default
Gibolt
la source
Pensez-vous que votre réponse aura une meilleure maison ici? stackoverflow.com/questions/28548015/…
nabster
0

J'aime utiliser ce genre de processus pour analyser les commandes sous forme de chaînes dans les énumérations. J'ai normalement l'une des énumérations comme "inconnue", donc il est utile de la renvoyer lorsque les autres ne sont pas trouvées (même sur une base insensible à la casse) plutôt que null (ce qui signifie qu'il n'y a pas de valeur). C'est pourquoi j'utilise cette approche.

static <E extends Enum<E>> Enum getEnumValue(String what, Class<E> enumClass) {
    Enum<E> unknown=null;
    for (Enum<E> enumVal: enumClass.getEnumConstants()) {  
        if (what.compareToIgnoreCase(enumVal.name()) == 0) {
            return enumVal;
        }
        if (enumVal.name().compareToIgnoreCase("unknown") == 0) {
            unknown=enumVal;
        }
    }  
    return unknown;
}
John Hemming
la source
-1

Le moyen le plus rapide pour obtenir le nom d'énumération est de créer une carte du texte et de la valeur d'énumération au démarrage de l'application, et pour obtenir le nom, appelez la fonction Blah.getEnumName ():

public enum Blah {
    A("text1"),
    B("text2"),
    C("text3"),
    D("text4");

    private String text;
    private HashMap<String, String> map;
    Blah(String text) {
    this.text = text;
    }

    public String getText() {
      return this.text;
    }

    static{
      createMapOfTextAndName();
    }

    public static void createMapOfTextAndName() {
        map = new HashMap<String, String>();
        for (Blah b : Blah.values()) {
             map.put(b.getText(),b.name());
        }
    }
    public static String getEnumName(String text) {
        return map.get(text.toLowerCase());
    } 
}
Bishal Jaiswal
la source