Cómo obtener un valor de enumeración de un valor de cadena en Java

2163

Digamos que tengo una enumeración que es solo

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

y me gustaría encontrar el valor de enumeración de una cadena, por ejemplo, "A"cuál sería Blah.A. ¿Cómo sería posible hacer esto?

¿Es el Enum.valueOf()método que necesito? Si es así, ¿cómo usaría esto?

0
2447

Sí, Blah.valueOf("A")te lo daré Blah.A.

Tenga en cuenta que el nombre debe ser una coincidencia exacta , incluido el caso: Blah.valueOf("a")y Blah.valueOf("A ")ambos arrojan un IllegalArgumentException.

Los métodos estáticos valueOf()y values()se crean en tiempo de compilación y no aparecen en el código fuente. Sin embargo, sí aparecen en Javadoc; por ejemplo, Dialog.ModalityTypemuestra ambos métodos.

11
  • 108
    Como referencia, el Blah.valueOf("A")método distingue entre mayúsculas y minúsculas y no tolera espacios en blanco extraños, por lo tanto, la solución alternativa propuesta a continuación por @ JoséMi. Brett 17 de diciembre de 2013 a las 17:37
  • 3
    @Michael Myers, dado que esta respuesta es la más votada con diferencia, ¿debo entender que es una buena práctica definir una enumeración y su valor de cadena para que sean exactamente iguales? Kevin Meredith 12 feb 2014 a las 21:24
  • 4
    @KevinMeredith: Si te refieres al toString()valor, no, no diría eso. name()obtendrá el nombre real definido de la constante de enumeración a menos que lo anule. Michael Myers 14 de febrero de 2014 a las 4:10
  • 4
    ¿Qué quiere decir exactamente con "se crean en tiempo de compilación y no aparecen en el código fuente"? ? treesAreEverywhere 7 de marzo de 2014 a las 0:43
  • 8
    @treesAreEverywhere Más específicamente, esos métodos son generados (o sintetizados ) por el compilador. La enum Blah {...}definición actual no debería intentar declararse propia valuesni valuesOf. Es como escribir "AnyTypeName.class" aunque nunca haya declarado una variable miembro de "clase"; el compilador hace que todo funcione. (Es posible que esta respuesta ya no le sea útil 3 meses después, pero por si acaso)Ti Strga 30 de mayo de 2014 a las 19:31
949

Otra solución si el texto no es el mismo que el valor de enumeración:

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;
    }
}
23
  • 434
    throw new IllegalArgumentException("No constant with text " + text + " found")sería mejor que return null. whiskeysierra 31/07/10 a las 10:28
  • 8
    @whiskeysierra Jon Skeet no estaría de acuerdo con eso. stackoverflow.com/questions/1167982/…Sanghyun Lee 5 de septiembre de 2011 a las 4:06
  • 14
    @Sangdol ¿ Podrías aclararnos por qué devolver nulo es mejor? whiskeysierra 29/09/11 a las 15:04
  • 63
    @Sangdol por lo general es bueno comprobar lo que hace SUN, oops, Oracle en la misma situación. Y como Enum.valueOf () muestra que ES la mejor práctica lanzar una Excepción en este caso. Porque es una situación excepcional. La "optimización del rendimiento" es una mala excusa para escribir código ilegible ;-)raudi 2 de febrero de 2012 a las 7:43
  • 5
    Bueno, también puedes hacer uso de la anotación @Nullable para que sea "legible" ;-)JoséMi 3 de febrero de 2012 a las 8:20
134

Utilice el patrón de Joshua Bloch , Effective Java :

(simplificado por brevedad)

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().toLowerCase(),instance);
        }
        ENUM_MAP = Collections.unmodifiableMap(map);
    }

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

Ver también:

Ejemplo de Oracle Java usando Enum y Map de instancias

Orden de ejecución de bloques estáticos en un tipo Enum

¿Cómo puedo buscar una enumeración de Java a partir de su valor de cadena?

dieciséis
  • 7
    si Joshua Bloch lo dijo, entonces este es el único camino a seguir :-). Es una pena que siempre tenga que desplazarme hacia abajo. dermoritz 10 de ene. De 2017 a las 9:51
  • 14
    Esto es aún más simple en Java 8 como puede hacer: Stream.of(MyEnum.values()).collect(toMap(Enum::name, identity()))también recomiendo anular toString () (pasado a través del constructor) y usar eso en lugar del nombre, especialmente si la Enum está asociada con datos serializables, ya que esto le permite controlar la carcasa sin dar Sonar un ataque. Novaterata 27/04/2017 a las 17:01
  • 1
    Java 8 ciertamente puede / cambiará muchas (mejores) respuestas en este foro. Sin embargo, no estoy seguro de que la cola (Sonar) mueva al perro (código de aplicación). Darrell Teague 28/04/2017 a las 20:31
  • 4
    Si lo va a poner de unmodifiableMaptodos modos, entonces no hay ningún beneficio en comenzar con un ConcurrentHashMap. Solo usa un HashMap. (¡Si tienes Guayaba, ImmutableMapte lo recomendaría!)Daniel Pryden 8 de marzo de 2018 a las 16:13
  • 11
    La inicialización estática está intrínsecamente sincronizada , por lo que no hay absolutamente ninguna razón para usar ConcurrentHashMapaquí, donde el mapa nunca se modifica después de la inicialización. De ahí por qué incluso, por ejemplo, el ejemplo en el propio JLS utiliza un archivo regular HashMap. Radiodef 5/08/18 a las 17:24
130

Aquí hay una ingeniosa utilidad que utilizo:

/**
 * 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;
}

Luego, en mi clase de enumeración, generalmente tengo esto para ahorrar algo de escritura:

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

Si sus enumeraciones no son todas en mayúsculas, simplemente cambie la Enum.valueOflínea.

Es una lástima que no puedo utilizar T.classpara Enum.valueOfque Tse borra.

15
  • 188
    Ese bloque de captura vacío realmente me vuelve loco, lo siento. whiskeysierra 31/07/10 a las 10:14
  • 33
    @LazloBonin: Las excepciones son para condiciones excepcionales, no para control de flujo. Consiga una copia de Effective Java . Martin Schröder 15/11/11 a las 17:09
  • 10
    Si la API de Java que desea usar arroja una excepción y no desea que su código arroje una, puede tragarse la excepción de esta manera o reescribir la lógica desde cero para que no se lance ninguna excepción en primer lugar. Tragar la excepción es a menudo el mal menor. Nate C-K 30/11/11 a las 19:26
  • 47
    ¡Horrible! Siempre, siempre detecte las excepciones donde pueda manejarlas. El ejemplo anterior es un ejemplo perfecto de cómo NO hacerlo . ¿Por qué? Por lo tanto, devuelve NULL, y la persona que llama tiene que verificar NULL o lanzar una NPE. Si la persona que llama sabe cómo manejar la situación, entonces hacer un if vs try-catch puede parecer un poco más elegante, PERO si no puede manejar, tiene que pasar nulo nuevamente y la persona que llama nuevamente tiene que verificar con NULL. , etc., etc.raudi 2 de febrero de 2012 a las 7:52
  • 10
    Para ser justos con la solución anterior, realmente hay casos de usos que requieren que devuelva nulo en lugar de lanzar IllegalArgumentException y romper el flujo de su programa, por ejemplo, asignando enumeraciones entre un esquema de servicio web y un esquema de base de datos en los que no siempre son uno. -a uno. Sin embargo, estoy de acuerdo en que el bloque de captura nunca debe dejarse vacío. Ponga algún código como log.warn o algo con fines de seguimiento. Adrian M 6 de febrero de 2012 a las 8:19
78

También debe tener cuidado con su caso. Déjame explicarte: hacer Blah.valueOf("A")funciona, pero Blah.valueOf("a")no funcionará. Entonces otra vez Blah.valueOf("a".toUpperCase(Locale.ENGLISH))funcionaría.

En Android deberías usar Locale.US, como señala sulai .

4
  • 6
    ¡Tenga cuidado con la configuración regional predeterminada! tc. 28/11/12 a las 15:43
  • 4
    Para los usuarios de Android, me gustaría señalar que la documentación de Android fomenta explícitamente el uso de Locale.USpara entrada / salida legible por máquina. sulai 2 de septiembre de 2013 a las 17:44
  • 2
    @Trengot Sí, lamentablemente. Turquía es un buen ejemplo. Combine esto con el manejo defectuoso de Java de los juegos de caracteres predeterminados (por defecto es latín en Windows en lugar de Unicode) y encontrará que casi siempre es inseguro usar las versiones predeterminadas de métodos que aceptan un juego de caracteres o una configuración regional. Casi siempre debería definirlos explícitamente. Stijn de Witt 26/07/2015 a las 13:34
  • No estoy seguro de que los conjuntos de caracteres "predeterminados" de Java y otros estén "rotos" per se, pero se otorgan, el uso predeterminado de UTF-8 en lugar de anulaciones (que deben hacerse siempre para ser explícitos) habría sido mejores sistemas para programadores junior que comúnmente no comprenden conceptos de juego de caracteres. Darrell Teague 29 de mayo de 2018 a las 20:45
48

En Java 8 o posterior, usando 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();
    }
}
2
  • No estoy seguro de que esta sea una mejor respuesta. En este caso, los flujos son un iterador de un solo subproceso como cualquier otro sobre todos los valores y posiblemente menos rendimiento que un impl de búsqueda de mapa. Las secuencias tienen más valor en un contexto de subprocesos múltiples donde, por ejemplo, la ejecución paralela de un archivo de texto separado por líneas nuevas puede mejorar el rendimiento. Darrell Teague 15 abr.20 a las 22:03
  • @DarrellTeague Si mide la diferencia de nanosegundos entre la solución Streams y la solución de búsqueda de mapas, verá una diferencia. Sin embargo, creo que en el mundo real no habrá una diferencia de rendimiento apreciable a menos que tenga una enumeración bastante grande, que es una excepción a la regla en mi opinión. Además, el enfoque de Streams es compacto y legible, aunque estoy seguro de que también podría presentar el mismo argumento para la implementación del mapaFlaom 15 de enero a las 20:59
40

Aquí hay un método que puede hacerlo para cualquier Enum y no distingue entre mayúsculas y minúsculas.

/** 
 * 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()
    ));
}
3
  • 1
    Esta variación lo está haciendo correctamente: equalsIgnoreCasees el camino a seguir. +1Stijn de Witt 26/07/2015 a las 13:35
  • Como la insensibilidad a mayúsculas y minúsculas, pero ... prefiero Enums sobre asignaciones de cadenas (aleatorias) para claves y ... menor, pero la iteración es menos eficaz para una búsqueda tan posiblemente repetitiva. Por lo tanto impl de EnumMap et al. Darrell Teague 13/08/18 a las 17:26
  • Esto no funciona ! Cambié equalsIgnoreCase a equals para mi propósito. El código falló a pesar de que ambas entradas a iguales eran exactamente iguales. MasterJoe 26/11/18 a las 20:49
37

Lo Blah.valueOf(string)mejor es usarlo, pero también puede usarlo Enum.valueOf(Blah.class, string).

3
  • 1
    Sensible a mayúsculas y minúsculas, ¡no ayuda! Murtaza Kanchwala 28/06/15 a las 18:58
  • @MurtazaKanchwala ¿Puedes aclarar tu comentario? ¿Que estás tratando de hacer? Peter Lawrey 29/06/15 a las 10:18
  • 2
    Hola @PeterLawrey, estaba tratando de obtener una enumeración de una cadena pública enum ObjectType {PERSON ("Persona") public String parameterName; ObjectType (String parameterName) {this.parameterName = parameterName; } public String getParameterName () {return this.parameterName; } public static ObjectType fromString (String parameterName) {if (parameterName! = null) {for (ObjectType objType: ObjectType.values ​​()) {if (parameterName.equalsIgnoreCase (objType.parameterName)) {return objType; }}} devuelve nulo; }}Murtaza Kanchwala 29/06/15 a las 17:45
33

Mis dos centavos aquí: usar Java 8 Streams y verificar una cadena exacta:

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

Cambié el nombre de la función a, fromString()ya que al nombrarla usando esa convención, obtendrá algunos beneficios del lenguaje Java en sí; por ejemplo:

  1. Conversión directa de tipos en la anotación HeaderParam
5
  • 1
    Alternativamente, para permitirle escribir switchbloques más legibles , puede en .orElse(null)lugar de .orElseThrow()codificar la excepción en la defaultcláusula e incluir más información útil cuando sea necesario. Y para hacerlo más indulgente, podría usarv -> Objects.equals(v.name, s == null ? "" : s.trim().toUpperCase())Adam 10 de mayo de 2018 a las 11:35
  • o simplemente devolver el Optionalde findFirst(), permitiendo al usuario decidir si quiere .orElse(null), orElseThrow()o lo que sea ....user85421 2/08/18 a las 20:58
  • 1
    Declarar a public static MyEnum valueOf(String)es en realidad un error de compilación, ya que es el mismo que el definido implícitamente, por lo que la versión anterior de su respuesta es en realidad mejor. ( jls , ideone )Radiodef 23 dic 2018 a las 23:34
  • En mi opción, es mejor evitar las Excepciones y usar Opcional. Además, deberíamos anular null y usar Optional en su lugar también. Hans Schreuder 23 de mayo de 2019 a las 10:57
  • Nuevamente, recuerde que incluso si el código tiene menos o mejor apariencia ... una implementación de Stream como esta es solo un iterador sobre todos los valores versus una búsqueda de mapa (menos rendimiento). Darrell Teague 15 abr.20 a las 22:05
30

Si no desea escribir su propia utilidad, utilice la de Google Biblioteca:

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

A diferencia de la función incorporada de Java, le permite verificar si A está presente en Blah y no lanza una excepción.

2
  • 7
    La parte triste es que esto devuelve un Opcional de Google y no un Opcional de JavajavaProgrammer 19 de agosto de 2016 a las 0:02
  • Verdadero. Sin embargo, exultante. Google y Netflix tienen excelentes bibliotecas de Java. Donde hay una superposición con las clases de actualización de Java implementadas en versiones más nuevas, inevitablemente causa problemas. Algo que tiene que ser todo en un proveedor lib. Darrell Teague 13/08/18 a las 17:30
17

Puede que necesite esto:

public enum ObjectType {
    PERSON("Person");

    public String parameterName;

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

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

    // From the String method, it 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;
    }
}

Una adición más

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

Esto le devolverá el valor por un nombre de enumeración en cadena. Por ejemplo, si proporciona "PERSON" en fromEnumName, le devolverá el valor de Enum, es decir, "Person".

0
14

Otra forma de hacer esto es usando el método estático implícito name()de Enum. name devolverá la cadena exacta que se usó para crear esa enumeración que se puede usar para verificar la cadena proporcionada:

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

Pruebas:

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


// It will print B  B

Inspiración: 10 ejemplos de Enum en Java

3
  • 7
    Esto es esencialmente lo que valueOfhace por ti. Este método estático no ofrece nada adicional, excepción y todo. Entonces, las construcciones if / else son altamente peligrosas ... cualquier nueva constante de enumeración agregada hará que este método se rompa sin cambios. YoYo 30/09/12 a las 18:01
  • Considere también este ejemplo de cómo podemos usar valueOf para realizar una búsqueda que no distingue entre mayúsculas y minúsculas, o cómo podemos evitar su excepción y emplear alias para proporcionar nombres alternativos: stackoverflow.com/a/12659023/744133YoYo 30/09/12 a las 18:18
  • 2
    name()no es estático. nrubin29 21/04/2014 a las 0:39
12

Aquí hay una solución que usa las bibliotecas de Guava . El método getPlanet () no distingue entre mayúsculas y minúsculas, por lo que getPlanet ("MerCUrY") devolverá 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();
   }
}
1
  • ¿Qué pasa con Plutón? Priidu Neemre 11 de marzo a las 7:56
9

En Java 8, el patrón de mapa estático es aún más fácil y es mi método preferido. Si desea usar Enum con Jackson, puede anular toString y usar eso en lugar del nombre, luego anotar con@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);
    }
}
3
  • Jackson es una implementación JSON (JavaScript Object Notation). La pregunta original no tenía nada que ver con JSON. Darrell Teague 15 abr.20 a las 22:06
  • La parte JSON era solo un material adicional que encontré relevante en ese momento, ya que obtener un Enum de una cadena es básicamente un tipo de deserialización y JSON / Jackson es probablemente la solución de serialización más popular. Novaterata 16 abr.20 a las 1:38
  • Comprender, pero desde el punto de vista de la moderación, no contribuyó a responder la pregunta del OP, así que solo trato de ayudar a establecer el contexto allí.JSON es de hecho el camino a seguir para convertir objetos a forma canónica en Java con Jackson como una gran biblioteca. Darrell Teague 17 abr.20 a las 12:50
9

Enum es muy útil. He estado usando Enummucho para agregar una descripción para algunos campos en diferentes idiomas, como el siguiente ejemplo:

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

Y luego puede recuperar la descripción de forma dinámica basada en el código de idioma pasado al getDescription(String lang)método, por ejemplo:

String statusDescription = Status.valueOf("ACT").getDescription("en");
1
  • 1
    Buen ejemplo de impulsar Enums aún más. Habría hecho la codificación del idioma usando los nombres estáticos estándar y la búsqueda en un mapa, pero aún así ... un buen ejemplo de tener una enumeración con diferentes etiquetas para lo que es esencialmente el mismo valor lógico. Darrell Teague 15 abr.20 a las 22:11
8

Para agregar a las respuestas anteriores y abordar algunas de las discusiones sobre nulos y NPE , estoy usando Guava Optionals para manejar casos ausentes / inválidos. Esto funciona muy bien para el análisis de URI y parámetros.

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

Para aquellos que no lo saben, aquí hay más información sobre cómo evitar nulos con Opcional .

1
  • Esta es una respuesta realmente buena para los patrones relacionados y el uso de Optional dentro de una Enum, aprovechando el hecho de que las Enums también son clases y, por lo tanto, pueden decorarse con métodos, métodos de anulación, etc. También es un buen caso para la programación de estilo Fluent ya que Los nulos devueltos por los métodos hacen que la construcción tenga errores (NPE en lugares desconocidos en las cadenas de Fluent de llamadas a métodos). Darrell Teague 15 abr.20 a las 22:09
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;
}
3
  • 1
    Eche un vistazo a este enlace para obtener guías sobre cómo responder y hacer preguntas en stackoverflow.com: stackoverflow.com/faqbakoyaro 9/11/11 a las 18:15
  • 2
    Eso es más o menos lo mismo que la respuesta de JoséMiRup 14/11/11 a las 12:40
  • Una explicación estaría en orden. Responda editando (cambiando) su respuesta , no aquí en los comentarios ( sin "Editar:", "Actualizar:" o similar; la respuesta debería aparecer como si estuviera escrita hoy). Peter Mortensen 10 de agosto a las 17:39
6

Un método O (1) inspirado en el código generado por Thrift que usa un mapa de hash.

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

}
1
  • 1
    Tenga en cuenta que los identificadores cero (0) y uno (1) son innecesarios. El método Enum values ​​() devolverá los miembros en el mismo orden en que se codificaron. Así, la primera entrada será cero ordinal, la segunda, etc.Darrell Teague 15 abr.20 a las 22:13
5

La biblioteca commons-lang de Apache tiene una función estática org.apache.commons.lang3.EnumUtils.getEnum que asignará una cadena a su tipo Enum. Básicamente, la misma respuesta que la de Geoffrey Zheng , pero no es necesario que lances la tuya propia cuando ya está en la naturaleza.

2
  • 1
    Comentario justo (DRY) pero ... aunque la mayoría de las cosas de Apache Commons son geniales, yo mismo encontré varios errores y anti-patrones en esa base. Por lo tanto, refiriéndose a decir que la implementación de Joshua Bloch puede tener una base más sólida. Luego se reduce a tener que revisar el código de Apache para saber qué implementó alguien. Si fuera el famoso Doug Leah quien reescribió la concurrencia de Java ... entonces confiaría implícitamente en él. Darrell Teague 15 abr.20 a las 22:16
  • El primer vínculo está roto: "No encontrado. No se encontró la URL solicitada en este servidor". Peter Mortensen 10 de agosto a las 17:53
5

Usar:

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

java.lang.Enum define varios métodos útiles, que están disponibles para todos los tipos de enumeración en Java:

  • Puede usar el name()método para obtener el nombre de cualquier constante Enum. El literal de cadena utilizado para escribir constantes de enumeración es su nombre.
  • De manera similar, el values()método se puede usar para obtener una matriz de todas las constantes Enum de un tipo Enum.
  • Y para la pregunta formulada, puede usar el valueOf()método para convertir cualquier String en una constante Enum en Java, como se muestra a continuación.
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

En este fragmento de código, el valueOf()método devuelve una constante Enum, Gender.MALE , y el nombre de la llamada que devuelve "MALE".

4

Añadiendo a la respuesta de Michael Myers , con una utilidad útil ...

valueOf() arroja dos excepciones diferentes en los casos en que no le gusta su entrada.

  • IllegalArgumentException
  • NullPointerExeption

Si sus requisitos son tales que no tiene ninguna garantía de que su String definitivamente coincidirá con un valor de enumeración, por ejemplo, si los datos de String provienen de una base de datos y podrían contener una versión anterior de la enumeración, entonces deberá manejar estos a menudo...

Así que aquí hay un método reutilizable que escribí que nos permite definir una enumeración predeterminada que se devolverá si la cadena que pasamos no coincide.

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

Úselo así:

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
}
3

Como switchaún no se ha mencionado una versión, la presento (reutilizando la enumeración de 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);
      }
    }
  }

Dado que esto no le da ningún valor adicional al valueOf(String name)método, solo tiene sentido definir un método adicional si queremos tener un comportamiento diferente. Si no queremos generar un IllegalArgumentException, podemos cambiar la implementación a:

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

Al aportar un valor por defecto mantenemos el contrato de Enum.valueOf(String name)sin tirar IllegalArgumentException y de tal forma que en ningún caso nullse devuelve. Por lo tanto, arrojamos un NullPointerExceptionsi el nombre es nully en el caso de defaultsi defaultValuees null. Así es como valueOfOrDefaultfunciona.

Este enfoque adopta el diseño de la Mapinterfaz que proporciona un método a Map.getOrDefault(Object key, V defaultValue)partir de Java 8.

2
  • switchy OOP ? Peter Mortensen 10 de agosto a las 18:04
  • @PeterMortensen: La pregunta del OP no se limita a OOP. Se refiere a Java. LuCio 11 de agosto a las 6:12
2

Estaba buscando una respuesta para encontrar el nombre "bla" y no su valor (no el texto). Según la respuesta de Manu , encuentro útil este código:

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 valueOfCode(String blahCode) throws IllegalArgumentException {
    Blah blah = Arrays.stream(Blah.values())
            .filter(val -> val.name().equals(blahCode))
            .findFirst()
            .orElseThrow(() -> new IllegalArgumentException("Unable to resolve blah: " + blahCode));

    return blah;
}

}

1
  • No creo que esto siquiera se compile (el resaltado de sintaxis también proporciona una pista). Peter Mortensen 10 de agosto a las 18:09
1

Otra utilidad que captura a la inversa. Usando un valor que identifique esa enumeración, no por su nombre.

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

Ejemplo:

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")devuelve Foo.THREE, porque se utilizará getValuepara coincidir con "drei", que es un método público único, no final y no estático en Foo. En caso de Foo tiene más de método público, no es definitiva y no estática, por ejemplo, getTranslateque devuelve "Drei", el otro método puede ser utilizado: EnumUtil.from(Foo.class, "drei", "getTranslate").

1

Enum valueOf ()

Una clase enum obtiene automáticamente un método valueOf () estático en la clase cuando se compila. El método valueOf () se puede usar para obtener una instancia de la clase enum para un valor String dado.

Por ejemplo:

public class Main {
    public static void main(String[] args) throws Exception {
        System.out.println(Strings.TWO.name());
    }
    enum Strings {
        ONE, TWO, THREE
    }
}
1
0

Me gusta usar este tipo de proceso para analizar comandos como cadenas en enumeraciones. Normalmente tengo una de las enumeraciones como "desconocida", por lo que es útil que se devuelva cuando las otras no se encuentran (incluso sin distinción entre mayúsculas y minúsculas) en lugar de nula (lo que significa que no hay valor). Por eso utilizo este enfoque.

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;
}
1
  • ¿Qué se supone que haga? ¿Como funciona? ¿Cuál es la esencia / idea? Responda editando (cambiando) su respuesta , no aquí en los comentarios ( sin "Editar:", "Actualizar:" o similar; la respuesta debería aparecer como si estuviera escrita hoy). Peter Mortensen 10 de agosto a las 17:58
0

Solución de Kotlin

Cree una extensión y luego llame valueOf<MyEnum>("value"). Si el tipo no es válido, obtendrá nulo y tendrá que manejarlo

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

Alternativamente, puede establecer un valor predeterminado, llamar valueOf<MyEnum>("value", MyEnum.FALLBACK)y evitar una respuesta nula. Puede extender su enumeración específica para que el valor predeterminado sea automático

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

O si quieres ambos, haz el segundo:

inline fun <reified T : Enum<T>> valueOf(type: String, default: T): T = valueOf<T>(type) ?: default
3
  • ¿Crees que tu respuesta tendrá un mejor hogar aquí? stackoverflow.com/questions/28548015/…nabster 5 de mayo de 2020 a las 22:39
  • 1
    Esta no es una pregunta de Kotlin. shinzou 18/06/20 a las 10:29
  • 1
    Esto es similar a Js / jQuery. Cuando los desarrolladores de Android buscan una solución, terminan buscando Java y luego traduciendo a Kotlin. Esta es una solución no obvia, mejor que una traducción directa. Los motores de búsqueda no dejarán de priorizar esta solución en el corto plazo, es mejor simplemente ayudar a esos desarrolladores a encontrar rápidamente una buena solución. Está marcado como Kotlin, los desarrolladores de Java pueden omitirloGibolt 18/06/20 a las 16:06
0

La forma más rápida de obtener el nombre de la enumeración es crear un mapa de texto y valores de enumeración cuando se inicia la aplicación, y para obtener el nombre, llame a la función 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());
    }
}
0

Blah.valueOf("A")es la declaración que está buscando, pero tenga en cuenta que esto es SENSIBLE A MAYÚSCULAS, por lo que Blah.valueOf ("a") no funcionará y generará una excepción.