Como obter um valor de enum a partir de um valor de string em Java?


1984

Digamos que eu tenho uma enumeração que é apenas

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

e eu gostaria de encontrar o valor enum de uma string, por exemplo, "A"qual seria Blah.A. Como seria possível fazer isso?

É o Enum.valueOf()método que eu preciso? Se sim, como eu usaria isso?

Respostas:


2259

Sim, Blah.valueOf("A")vou te dar Blah.A.

Observe que o nome deve ser uma correspondência exata , incluindo maiúsculas Blah.valueOf("a")e minúsculas: e Blah.valueOf("A ")ambos jogam um IllegalArgumentException.

Os métodos estáticos valueOf()e values()são criados no momento da compilação e não aparecem no código-fonte. Eles aparecem no Javadoc; por exemplo, Dialog.ModalityTypemostra os dois métodos.


100
Para referência, o Blah.valueOf("A")método diferencia maiúsculas de minúsculas e não tolera espaços em branco estranhos, portanto, a solução alternativa proposta abaixo por @ JoséMi.
Brett

3
@ Michael Myers, Como essa resposta é a mais votada de longe, devo entender que é uma boa prática definir uma enumeração e seu valor String para ser exatamente o mesmo?
22814 Kevin Meredith

4
@ KevinMeredith: Se você quer dizer o toString()valor, não, eu não diria isso. name()obterá o nome definido real da constante enum, a menos que você o substitua.
Michael Myers

3
O que exatamente você quer dizer com "é criado em tempo de compilação e não aparece no código-fonte". ?
treesAreEverywhere

8
@treesAreEverywhere Mais especificamente, esses métodos são gerados (ou sintetizados ) pelo compilador. A enum Blah {...}definição real não deve tentar declarar a sua própria valuesnem valuesOf. É como você pode escrever "AnyTypeName.class" mesmo que nunca tenha declarado uma variável de membro "class"; o compilador faz tudo funcionar. (Esta resposta não pode ser útil para você 3 meses mais tarde, mas apenas no caso.)
Ti Strga

864

Outra solução se o texto não for igual ao valor da enumeração:

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")seria melhor que return null.
Whiskeyierra 31/07

8
Jon Whiskeysierra Jon Skeet não concorda com isso. stackoverflow.com/questions/1167982/…
Sanghyun Lee 5/11

11
@Sangdol Você poderia nos esclarecer por que retornar nulo é melhor?
Whiskeyierra

57
@ Sangdol geralmente é bom verificar o que o SUN - oops - a Oracle está fazendo na mesma situação. E como Enum.valueOf () está mostrando que é uma boa prática lançar uma exceção nesse caso. Porque é uma situação excepcional. "Otimização de desempenho" é uma má desculpa para escrever código ilegível ;-)
raudi

5
Bem, você também pode fazer uso de anotação @Nullable para torná-lo "legível" ;-)
Josemi

121

Aqui está um utilitário bacana que eu 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;
}

Então, na minha classe enum, geralmente tenho isso para salvar algumas digitações:

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

Se suas enumerações não são todas maiúsculas, basta alterar a Enum.valueOflinha.

Pena que eu não posso usar T.classpara Enum.valueOfcomo Té apagada.


176
Esse bloco vazio realmente me deixa louco, desculpe.
Ubuntu19

32
@ LazloBonin: Exceções são para condições excepcionais, não para controle de fluxo. Obtenha uma cópia do Java eficaz .
Restabeleça Monica - M. Schröder

10
Se a API Java que você deseja usar gera uma exceção e não deseja que seu código gere uma, você pode engolir a exceção dessa maneira ou reescrever a lógica do zero, para que nenhuma exceção seja lançada. Engolir a exceção geralmente é o mal menor.
Nate CK

47
Horrível! Sempre, sempre pegue exceções nas quais você pode lidar com elas. O exemplo acima é um exemplo perfeito de como NÃO fazer isso . Por quê? Portanto, ele retorna NULL, e o chamador deve verificar contra NULL ou lançar um NPE. Se o chamador sabe como lidar com a situação, executar um if vs. try-catch pode parecer um pouco mais elegante, mas se ele não puder lidar, ele precisará passar nulo novamente e o chamador novamente precisará verificar NULL. , etc etc.
raudi 02/02/2012

10
Para ser justo com a solução acima, existem realmente casos de uso que exigem que você retorne nulo em vez de lançar IllegalArgumentException e interrompa o fluxo do seu programa, por exemplo, mapeando enumerações entre um esquema de serviço da web e um esquema de banco de dados em que eles nem sempre são um -para um. No entanto, concordo que o bloco catch nunca deve ser deixado vazio. Coloque algum código como log.warn ou algo para fins de rastreamento.
Adrian M

101

Use o padrão de Joshua Bloch, Java Efetivo :

(simplificado por questões de brevidade)

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

Veja também:

Exemplo de Oracle Oracle usando Enum e mapa de instâncias

Ordem de execução de blocos estáticos em um tipo Enum

Como posso procurar uma enumeração Java a partir do seu valor String


4
se Joshua Bloch disse isso, este é o único caminho a seguir :-). É uma pena que eu sempre tenha que rolar até aqui.
Dermoritz

11
Isso é ainda mais simples no Java 8, como você pode fazer: Stream.of(MyEnum.values()).collect(toMap(Enum::name, identity()))eu também recomendo substituir toString () (transmitido pelo construtor) e usá-lo em vez de nome, especialmente se o Enum estiver associado a dados serializáveis, pois isso permite controlar a caixa sem fornecer Sonar um ajuste.
Novaterata 27/04

1
O Java 8 certamente pode / irá alterar muitas respostas (melhores) neste fórum. Porém, não tenho certeza de ter o rabo (Sonar) abanando o cachorro (código do aplicativo).
Darrell Teague

3
Se você quiser colocá-lo de unmodifiableMapqualquer maneira, não há nenhum benefício em começar com a ConcurrentHashMap. Basta usar um HashMap. (Se você tiver Goiaba do ImmutableMapentão eu recomendo que em vez!)
Daniel Pryden

9
A inicialização estática é inerentemente sincronizada , portanto não há razão para usar ConcurrentHashMapaqui, onde o mapa nunca é modificado após a inicialização. Por isso, mesmo que, por exemplo, o exemplo no próprio JLS use um regular HashMap.
Radiodef 5/08

74

Você também deve ter cuidado com o seu caso. Deixe-me explicar: fazer Blah.valueOf("A")obras, mas Blah.valueOf("a")não vai funcionar. Então, novamente Blah.valueOf("a".toUpperCase(Locale.ENGLISH))funcionaria.

editar
Alterado toUpperCasepara com toUpperCase(Locale.ENGLISH)base em tc. comentar e os documentos java

edit2 No Android, você deve usar Locale.US, como sulai aponta .


6
Cuidado com o código do idioma padrão!
tc.

3
Para os usuários do Android, gostaria de salientar que a documentação do Android incentiva explicitamente o uso de Locale.USentradas / saídas legíveis por máquina.
sulai 02/09

2
@Trengot Sim, infelizmente. A Turquia é um bom exemplo. Combine isso com o manuseio quebrado de conjuntos de caracteres padrão do Java (o padrão é Latin no Windows em vez de Unicode) e você quase sempre não é seguro usar as versões padrão dos métodos que aceitam um conjunto de caracteres ou local. Você quase sempre deve defini-los explicitamente.
Stijn de Witt

Não se os charsets "padrão" de Java et al são "quebrados" por si só, mas garantidos, o padrão de UTF-8 em vez de substituições (o que deve ser feito sempre para ser explícito) teria criado melhores sistemas para programadores juniores que geralmente não conseguem entender conceitos de conjunto de caracteres.
Darrell Teague

38

Aqui está um método que pode fazer isso para qualquer Enum e não diferencia maiúsculas de 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()
    ));
}

Essa variação está fazendo corretamente: equalsIgnoreCaseé o caminho a percorrer. +1
Stijn de Witt

Como insensibilidade a maiúsculas, mas ... prefere Enums ao invés de (aleatórias) atribuições de String para chaves e ... menor, mas a iteração é menos eficiente para uma pesquisa tão repetitiva. Daí impl de EnumMap et al.
Darrell Teague

Isso não funciona ! Alterei o equalsIgnoreCase para igual para o meu propósito. O código falhou mesmo que as duas entradas iguais fossem exatamente iguais.
precisa saber é o seguinte

36

Usar Blah.valueOf(string)é melhor, mas você também pode usá-lo Enum.valueOf(Blah.class, string).


1
Caso sensível, não está ajudando!
Murtaza Kanchwala

@MurtazaKanchwala Você pode esclarecer seu comentário? O que você está tentando fazer?
Peter Lawrey

2
Olá @PeterLawrey, eu estava tentando buscar um Enum de um enum String public ObjectType {PERSON ("Person") public String parameterName; ObjectType (String parameterName) {this.parameterName = parameterName; } public String getParameterName () {retorne this.parameterName; } public estático ObjectType fromString (String parameterName) {if (parameterName! = null) {for (ObjectType objType: ObjectType.values ​​()) {if (parameterName.equalsIgnoreCase (objType.parameterName)) {return objType; }}} retornar nulo; }}
Murtaza Kanchwala

34

No Java 8 ou posterior, usando o 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();
    }
}

Não tenho certeza se esta é uma resposta melhor. Os fluxos nesse caso são um iterador de thread único como qualquer outro sobre todos os valores e, sem dúvida, menos desempenho que um implemento de pesquisa de mapa. Os fluxos têm mais valor em um contexto multithread em que, por exemplo, a execução paralela de um arquivo de texto separado por nova linha pode melhorar o desempenho.
Darrell Teague

28

Se você não quiser escrever seu próprio utilitário, use o biblioteca:

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

Ao contrário da função java incorporada, vamos verificar se A está presente em Blah e não gera uma exceção.


7
parte triste é, isso retorna um google Opcional e não java Opcional
javaProgrammer

Verdade. Exoected embora. Google e Netflix têm ótimas bibliotecas Java. Onde há sobreposição com as classes de recuperação do Java implementadas em versões mais novas, inevitavelmente causa problemas. É preciso que seja tudo incluído em uma lib de fornecedor.
Darrell Teague

26

Meus 2 centavos aqui: usando Java8 Streams + verificando uma string exata:

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

** EDIT **

Renomeada a função para que, fromString()desde o nome da convenção, você obtenha alguns benefícios da própria linguagem Java; por exemplo:

  1. Conversão direta de tipos na anotação HeaderParam

1
Como alternativa, para permitir que você escreva switchblocos mais legíveis , é possível, em .orElse(null)vez de .orElseThrow()codificar a exceção - na defaultcláusula - e incluir informações mais úteis quando necessário. E para torná-lo mais branda você poderia usarv -> Objects.equals(v.name, s == null ? "" : s.trim().toUpperCase())
Adam

ou apenas voltar a Optionalpartir findFirst(), permitindo ao usuário decidir se quer .orElse(null), orElseThrow()ou o que quer ....
user85421

1
Declarar a public static MyEnum valueOf(String)é realmente um erro de compilação, já que é o mesmo que o definido implicitamente, portanto, a versão mais antiga da sua resposta é realmente melhor. ( jls , ideone )
Radiodef 23/12/18

Na minha opção, é melhor evitar exceções e usar opcional. Além disso, devemos anular nulo e usar Opcional também.
Hans Schreuder

Mais uma vez, lembre-se de que, mesmo com um código com menos ou melhor aparência ... uma implementação de Stream como essa é apenas um iterador sobre todos os valores versus uma pesquisa de mapa (menos desempenho).
Darrell Teague

16

Você pode precisar disso:

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

Mais uma adição:

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

Isso retornará o valor por um nome de enum com string. Por exemplo, se você fornecer "PERSON" no fromEnumName, ele retornará o valor de enum, ou seja, "Person"


13

Outra maneira de fazer isso usando o método estático implícito name()do Enum. name retornará a string exata usada para criar a enumeração que pode ser usada para verificar a string fornecida:

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

Teste:

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

//it will print B  B

inspiration: 10 exemplos de enum em Java


7
Isto é essencialmente o que valueOffaz para você. Este método estático não oferece nada extra, exceção e tudo. Então as construções if / else são altamente perigosas ... qualquer nova constante de enum adicionada fará com que esse método seja interrompido sem alterações.
YoYo

Considere também este exemplo de como podemos usar valueOf para fazer uma pesquisa sem distinção entre maiúsculas e minúsculas ou como podemos evitar sua exceção e empregar aliases para fornecer nomes alternativos: stackoverflow.com/a/12659023/744133
YoYo

2
name()não é estático.
precisa saber é o seguinte

10

Solução usando bibliotecas Guava. O método getPlanet () não faz distinção entre maiúsculas e minúsculas; portanto, getPlanet ("MerCUrY") retornará 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 adicionar às respostas anteriores e abordar algumas das discussões sobre nulos e NPE, estou usando Guava Optionals para lidar com casos ausentes / inválidos. Isso funciona muito bem para análise de URI / parâmetro.

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 quem não está ciente, veja mais algumas informações sobre como evitar nulo com Opcional: https://code.google.com/p/guava-libraries/wiki/UsingAndAvoidingNullExplained#Optional


Essa é uma resposta muito boa para padrões relacionados e o uso de Opcional dentro de um Enum - aproveitando o fato de que os Enums também são classes e, portanto, podem ser decorados com métodos, métodos de substituição, etc. É um bom argumento para a programação no estilo Fluente também os nulos retornados dos métodos fazem com que o buggy de construção (NPE em locais desconhecidos nas cadeias fluentes de chamadas de método).
Darrell Teague

8

No Java 8, o padrão estático do Mapa é ainda mais fácil e é o meu método preferido. Se você deseja usar o Enum com Jackson, você pode substituir ToString e usá-lo em vez de nome, e anotar com@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 é uma implementação JSON (JavaScript Object Notation). A pergunta original não tinha nada a ver com JSON.
Darrell Teague

A parte JSON foi apenas um bônus que eu achei relevante na época, já que obter um Enum de uma string é basicamente um tipo de desserialização e JSON / Jackson é provavelmente a solução de serialização mais popular.
Novaterata 16/04

Entenda, mas pelo aspecto da moderação - não contribuiu para responder à pergunta do OP, apenas tentando ajudar a contextualizar o contexto.JSON é realmente o caminho a percorrer para converter objetos em formato canônico em Java, sendo Jackson uma ótima 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;
}

dê uma olhada neste link para guias em responder e fazer perguntas sobre stackoverflow.com: stackoverflow.com/faq
bakoyaro

1
Isso é mais ou menos o mesmo que a resposta de Josemi
Rup

6

O (1) método inspirado em códigos gerados em parcelas que utilizam um mapa 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;
        }

}

Observe que os identificadores zero (0) e um (1) são desnecessários. O método Enum values ​​() retornará os membros na mesma ordem em que foram codificados. Assim, a primeira entrada será zero ordinal, a segunda, etc.
Darrell Teague

6

Enum é muito útil, tenho usado Enummuito para adicionar uma descrição para alguns campos em diferentes idiomas, como no exemplo a seguir:

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

E então você pode recuperar a descrição dinamicamente com base no código do idioma passado ao getDescription(String lang)método, por exemplo:

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

1
Bom exemplo ao empurrar Enums ainda mais. Teria feito a codificação de linguagem usando os nomes estáticos padrão e a pesquisa em um mapa, mas ainda assim ... um bom exemplo de ter uma enumeração com tags diferentes para o que é essencialmente o mesmo valor lógico.
Darrell Teague

5

A respeito?

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 vários métodos úteis, que estão disponíveis para todos os tipos de enumeração em Java:

  • Você pode usar o name()método para obter o nome de quaisquer constantes Enum. O literal de string usado para escrever constantes enum é o nome deles.
  • Da mesma forma, o values()método pode ser usado para obter uma matriz de todas as constantes Enum de um tipo Enum.
  • E para a pergunta feita, você pode usar o valueOf()método para converter qualquer constante String para Enum em Java, como mostrado abaixo.
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

Nesse trecho de código, o valueOf()método retorna uma constante Enum Gender.MALE, chamando o nome nesse retorno "MALE".


4

A biblioteca commons-lang do Apache possui uma função estática org.apache.commons.lang3.EnumUtils.getEnum, que mapeará uma String para o seu tipo de Enum. A mesma resposta, essencialmente, como Geoffreys, mas por que rolar a sua própria quando já está lá fora?


1
Comentário justo (DRY), mas ... enquanto a maioria das coisas do Apache Commons é ótima, eu encontrei vários bugs e anti-padrões nessa base. Assim, referindo-se a dizer que a implementação de Joshua Bloch pode estar em pé de igualdade. Resume então ter que revisar o código do Apache para saber o que alguém implementou. Se fosse o famoso Doug Leah que reescrevesse a concorrência em Java ... então eu confiaria implicitamente.
Darrell Teague

4

Adicionando à resposta mais votada, com um utilitário útil ...

valueOf() lança duas exceções diferentes nos casos em que não gosta de sua entrada.

  • IllegalArgumentException
  • NullPointerExeption

Se seus requisitos forem tais que você não tenha garantia de que sua String definitivamente corresponderá a um valor de enum, por exemplo, se os dados da String vierem de um banco de dados e puderem conter uma versão antiga da enum, será necessário lidar com eles frequentemente...

Então, aqui está um método reutilizável que eu escrevi que permite definir um Enum padrão a ser retornado se a String que passarmos não corresponder.

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

Use-o assim:

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 uma switchversão-ainda não foi mencionada, eu a apresento (reutilizando a enumeração do 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);
      }
    }
  }

Como isso não dá nenhum valor adicional ao valueOf(String name)método, faz sentido definir um método adicional se quisermos ter um comportamento diferente. Se não queremos levantar umIllegalArgumentException , podemos alterar a implementação para:

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

Ao fornecer um valor padrão que manter o contrato de Enum.valueOf(String name)sem lançar uma IllegalArgumentException dessa maneira que em nenhum caso nullé devolvido. Portanto, lançamos um NullPointerExceptionse o nome é nulle, no caso de, defaultse defaultValueénull . É assim que valueOfOrDefaultfunciona.

Essa abordagem adota o design da Mapinterface - que fornece um método a Map.getOrDefault(Object key, V defaultValue)partir do Java 8.


1

Outro utilitário capturando de maneira inversa. Usando um valor que identifique esse Enum, não pelo nome.

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

Exemplo:

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")retorna Foo.THREE, porque será usado getValuepara corresponder ao "drei", que é um método público único, não final e não estático no Foo. No caso Foo tem mais do que na método público, não é final e não estática, por exemplo, getTranslateque retorna "Drei", o outro método pode ser usado: EnumUtil.from(Foo.class, "drei", "getTranslate").


1

Solução Kotlin

Crie um ramal e ligue valueOf<MyEnum>("value"). Se o tipo for inválido, você será nulo e precisará lidar com isso

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

Como alternativa, você pode definir um valor padrão, chamando valueOf<MyEnum>("value", MyEnum.FALLBACK)e evitando uma resposta nula. Você pode estender sua enum específica para que o padrão seja 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
    }
}

Ou, se você quiser os dois, faça o segundo:

inline fun <reified T : Enum<T>> valueOf(type: String, default: T): T = valueOf<T>(type) ?: default

Você acha que sua resposta terá uma casa melhor aqui? stackoverflow.com/questions/28548015/…
nabster

0

Eu gosto de usar esse tipo de processo para analisar comandos como seqüências de caracteres em enumerações. Normalmente, eu tenho uma das enumerações como "desconhecida", portanto, isso ajuda a retornar quando as outras não são encontradas (mesmo que não sejam sensíveis a maiúsculas e minúsculas) em vez de nulas (o que significa que não há valor). Portanto, eu uso essa abordagem.

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

A maneira mais rápida de obter o nome de enum é criar um mapa de texto e valor de enum quando o aplicativo é iniciado e, para obter o nome, chame a função 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());
    } 
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.