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


1984

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?

Respuestas:


2259

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, aparecen en Javadoc; por ejemplo, Dialog.ModalityTypemuestra ambos métodos.


100
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

3
@Michael Myers, dado que esta respuesta es la más votada, ¿debería entender que es una buena práctica definir una enumeración y que su valor de cadena sea exactamente el mismo?
Kevin Meredith

44
@KevinMeredith: Si te refieres al toString()valor, no, no diría eso. name()obtendrá el nombre real definido de la constante enum a menos que lo anule.
Michael Myers

3
¿Qué quiere decir exactamente con "se crean en tiempo de compilación y no aparecen en el código fuente"? ?
treesAreEverywhere

8
@treesAreEverywhere Más específicamente, esos métodos son generados (o sintetizados ) por el compilador. La enum Blah {...}definición real no debería tratar de declarar su propia valuesni valuesOf. Es como cómo puedes escribir "AnyTypeName.class" aunque nunca hayas 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

864

Otra solución si el texto no es igual al 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;
    }
}

396
throw new IllegalArgumentException("No constant with text " + text + " found")sería mejor que return null.
whiskeysierra

8
@whiskeysierra Jon Skeet no estaría de acuerdo con eso. stackoverflow.com/questions/1167982/…
Sanghyun Lee

11
@Sangdol Podría usted saque a la luz la razón por regresar nula es mejor?
whiskeysierra

57
@Sangdol, por lo general, es bueno comprobar lo que SUN - ¡Uy! - Oracle está haciendo en la misma situación. Y como Enum.valueOf () está mostrando ES mejores prácticas para 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

55
Bueno, también puedes usar la anotación @Nullable para que sea "legible" ;-)
JoséMi

121

Aquí hay una ingeniosa utilidad que uso:

/**
 * 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 guardar algo de escritura:

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

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

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


176
Ese bloqueo vacío realmente me vuelve loco, lo siento.
whiskeysierra

32
@LazloBonin: Las excepciones son para condiciones excepcionales, no para el flujo de control. Obtenga una copia de Effective Java .
Restablecer a Monica - M. Schröder

10
Si la API de Java que desea usar arroja una excepción y no desea que su código arroje una, puede tragar la excepción de esta manera o volver a escribir la lógica desde cero para que no se arroje ninguna excepción en primer lugar. Tragar la excepción es a menudo el mal menor.
Nate CK

47
¡Horrible! Siempre, siempre tome 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 debe verificar contra NULL o lanzar un 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, debe pasar nulo nuevamente y la persona que llama nuevamente debe verificar contra NULL , etc., etc.
raudi

10
Para ser justos con la solución anterior, realmente hay casos de uso 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 el que no siempre son uno -a uno. Sin embargo, estoy de acuerdo en que el bloque catch nunca debe dejarse vacío. Ponga un código como log.warn o algo para el seguimiento.
Adrian M

102

Use el patrón de Joshua Bloch, Java efectivo :

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

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

Ver también:

Ejemplo de Oracle Java con 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


44
Si Joshua Bloch lo dijo, entonces este es el único camino a seguir :-). Es una pena que siempre tenga que desplazarme hacia abajo aquí.
dermoritz

11
Esto es aún más simple en Java 8, ya que puede hacerlo: Stream.of(MyEnum.values()).collect(toMap(Enum::name, identity()))también recomiendo reemplazar toString () (pasado a través del constructor) y usarlo en lugar de nombre, especialmente si Enum está asociado con datos serializables, ya que esto le permite controlar la carcasa sin dar Sonar un ajuste.
Novaterata

1
Java 8 ciertamente puede / cambiará muchas (mejores) respuestas en este foro. Sin embargo, no estoy seguro de tener la cola (Sonar) moviendo al perro (código de aplicación).
Darrell Teague el

3
Si lo vas a poner de unmodifiableMaptodos modos, entonces no hay ningún beneficio en comenzar con un ConcurrentHashMap. Solo usa a HashMap. (Si tienes guayaba, ImmutableMap¡te lo recomendaría!)
Daniel Pryden

99
La inicialización estática está sincronizada inherentemente , 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 JLS en sí usa un regular HashMap.
Radiodef

74

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

editar
Cambiado toUpperCasea toUpperCase(Locale.ENGLISH)basado en tc. comentario y los documentos de Java

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


66
¡Tenga cuidado con la configuración regional predeterminada!
tc.

3
Para sus usuarios de Android, me gustaría señalar que la documentación de Android fomenta explícitamente el uso de Locale.USentradas / salidas legibles por máquina.
sulai

2
@Trengot Sí, desafortunadamente. Turquía es un buen ejemplo. Combine esto con el manejo roto de Java de los conjuntos de caracteres predeterminados (por defecto en 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 conjunto de caracteres o configuración regional. Casi siempre debes definirlos explícitamente.
Stijn de Witt

No estoy seguro de que los charsets "predeterminados" de Java y otros estén "rotos" per se, pero se conceden, por defecto a UTF-8 en lugar de anulaciones (que deben hacerse siempre para ser explícitos) habrían hecho mejores sistemas para los programadores junior que comúnmente no entienden conceptos de juego de caracteres.
Darrell Teague

38

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

Esta variación lo está haciendo correctamente: equalsIgnoreCasees el camino a seguir. +1
Stijn de Witt

Como la insensibilidad entre mayúsculas y minúsculas, pero ... prefiere Enums en lugar de asignaciones de cadenas (aleatorias) para claves y ... menores, pero la iteración es menos eficaz para una búsqueda tan posiblemente repetitiva. Por lo tanto, impl de EnumMap et al.
Darrell Teague el

Esto no funciona ! Cambié equalsIgnoreCase por igual para mi propósito. El código falló a pesar de que ambas entradas a iguales eran exactamente iguales.
MasterJoe2

36

Usar Blah.valueOf(string)es lo mejor, pero también puedes usarlo Enum.valueOf(Blah.class, string).


1
Caso sensible, no ayuda!
Murtaza Kanchwala

@MurtazaKanchwala ¿Puedes aclarar tu comentario? ¿Que estás tratando de hacer?
Peter Lawrey

2
Hola @PeterLawrey, estaba tratando de obtener una Enum de un String public enum ObjectType {PERSON ("Person") public String parameterName; ObjectType (String parameterName) {this.parameterName = parameterName; } public String getParameterName () {return this.parameterName; } ObjectType estático público fromString (String parameterName) {if (parameterName! = null) {for (ObjectType objType: ObjectType.values ​​()) {if (parameterName.equalsIgnoreCase (objType.parameterName)) {return objType; }}} return null; }}
Murtaza Kanchwala

35

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

No estoy seguro de que esta sea una respuesta mejor. Las secuencias en este caso son un iterador de un solo subproceso como cualquier otro sobre todos los valores y posiblemente menos rendimiento que una búsqueda de mapa impl. 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 una nueva línea puede mejorar el rendimiento.
Darrell Teague

28

Si no desea escribir su propia utilidad, use Google biblioteca:

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

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


77
Lo triste es que esto devuelve un Google Opcional y no Java Opcional
javaProgrammer

Cierto. Aunque exoected. Google y Netflix tienen excelentes bibliotecas Java. Donde hay superposición con las clases de actualización de Java implementadas en versiones más nuevas, inevitablemente causa problemas. Tiene que estar todo incluido en un proveedor lib.
Darrell Teague el

26

Mis 2 centavos aquí: usar Java8 Streams + 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));
    }
}

** EDITAR **

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

  1. Conversión directa de tipos en la anotación HeaderParam

1
Alternativamente, para permitirle escribir más switchbloques legibles , puede .orElse(null)hacerlo en lugar de .orElseThrow()para poder codificar la excepción default, incluir la cláusula e incluir más información útil cuando sea necesario. Y para hacerlo más indulgente, podrías usarlov -> Objects.equals(v.name, s == null ? "" : s.trim().toUpperCase())
Adam

o simplemente devolver el Optionalde findFirst(), permitiendo al usuario decidir si quiere .orElse(null), orElseThrow()o lo que sea ...
user85421

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 realmente mejor. ( jls , ideone )
Radiodef

En mi opción, es mejor evitar Excepciones y usar Opcional. Además de eso, debemos anular nulo y usar Opcional también.
Hans Schreuder

Una vez más, solo recuerde que incluso si el código tiene un aspecto menos o mejor ... 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

16

Es posible que necesite esto:

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

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 Enum. Stringified. Por ejemplo, si proporciona "PERSONA" en fromEnumName, le devolverá el Valor de Enum, es decir, "Persona"


13

Otra forma de hacerlo mediante el método estático implícito name()de Enum. name devolverá la cadena exacta utilizada para crear esa enumeración que se puede utilizar 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


77
Esto es esencialmente lo que valueOfhace por ti. Este método estático no ofrece nada extra, excepción y todo. Entonces las construcciones if / else son altamente peligrosas ... cualquier nueva constante enum agregada hará que este método se rompa sin cambios.
YoYo

Considere también este ejemplo de cómo podemos usar valueOf para hacer 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/744133
YoYo

2
name()No es estático.
nrubin29

10

Solución utilizando bibliotecas de guayaba. 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();
   }
}

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 / 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: https://code.google.com/p/guava-libraries/wiki/UsingAndAvoidingNullExplained#Optional


Esta es una muy buena respuesta para patrones relacionados y el uso de Opcional dentro de un Enum, aprovechando el hecho de que los Enums también son clases y, por lo tanto, se pueden decorar con métodos, métodos de anulación, etc. Es un buen caso para la programación de estilo fluido también desde los valores nulos devueltos de los métodos hacen que esa construcción tenga errores (NPE en lugares desconocidos en cadenas fluidas de llamadas a métodos).
Darrell Teague

8

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

Jackson es una implementación JSON (JavaScript Object Notation). La pregunta original no tenía nada que ver con JSON.
Darrell Teague

La parte de JSON era solo algo adicional que encontré relevante en ese momento, ya que obtener una 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

Comprenda, pero desde el punto de vista de la moderación: no contribuyó a responder la pregunta del OP, así que solo traté de ayudar a establecer el contexto allí. JSON es de hecho el camino a seguir para convertir objetos en forma canónica en Java con Jackson como una gran biblioteca.
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;
}

eche un vistazo a este enlace para obtener guías sobre
cómo

1
Eso es más o menos lo mismo que la respuesta de JoséMi
Rup

6

Método O (1) inspirado en el código generado por ahorro que utiliza un hashmap.

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

}

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 que los codificados. Por lo tanto, la primera entrada será el ordinal cero, la segunda, etc.
Darrell Teague

6

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 dinámicamente en función del código de idioma pasado al getDescription(String lang)método, por ejemplo:

String statusDescription = Status.valueOf("ACT").getDescription("en");

1
Buen ejemplo con empujar a Enums aún más. Habría hecho la codificación del idioma utilizando 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

5

¿Qué pasa?

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 todo tipo de enumeración en Java:

  • Puede usar el name()método para obtener el nombre de las constantes Enum. El literal de cadena utilizado para escribir constantes enum es su nombre.
  • De manera similar values(), se puede usar un método 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 cadena a 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, llamando al nombre en ese retorno "MALE".


4

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. Esencialmente, la misma respuesta que Geoffreys, pero ¿por qué tirar la tuya cuando ya está en la naturaleza?


1
Comentario justo (SECO) pero ... si bien la mayoría de las cosas de Apache Commons son geniales, he encontrado varios errores y antipatrones en esa base. Por lo tanto, en referencia a decir que la implementación de Joshua Bloch puede estar en una posición más sólida. Se reduce a tener que revisar el código de Apache para saber lo que alguien implementó. Si se dijera el famoso Doug Leah que reescribió la concurrencia de Java ... entonces confiaría implícitamente en ello.
Darrell Teague

4

Agregando a la respuesta mejor calificada, con una útil utilidad ...

valueOf() lanza dos Excepciones diferentes en casos donde no le gusta su entrada.

  • IllegalArgumentException
  • NullPointerExeption

Si sus requisitos son tales que no tiene ninguna garantía de que su String definitivamente coincida 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á manejarlos a menudo...

Así que aquí hay un método reutilizable que escribí que nos permite definir una Enum 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
}

2

Como switchtodavía 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 plantear una 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 proporcionar un valor por defecto mantenemos el contrato de Enum.valueOf(String name)sin lanzar una IllegalArgumentException de esa manera que en ningún caso nullse devuelve. Por lo tanto, arrojamos un NullPointerExceptionsi el nombre es nully en 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.


1

Otra utilidad que captura de forma inversa. Usando un valor que identifica esa enumeración, no de 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")regresa Foo.THREE, porque se usará getValuepara hacer coincidir "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

Solución Kotlin

Crea una extensión y luego llama 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 ampliar 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

¿Crees que tu respuesta tendrá un mejor hogar aquí? stackoverflow.com/questions/28548015/…
nabster

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 la devuelvan cuando no se encuentran las otras (incluso sin distinción de mayúsculas y minúsculas) en lugar de nulas (lo que significa que no hay valor). Por lo tanto, uso 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

La forma más rápida de obtener el nombre de enum es crear un mapa de texto y valor 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());
    } 
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.