Usando valores Enum como literais String

389

Qual é a melhor maneira de usar os valores armazenados em um Enum como literais String? Por exemplo:

public enum Modes {
    some-really-long-string,
    mode1,
    mode2,
    mode3
}

Mais tarde, eu poderia usar Mode.mode1para retornar sua representação de string como mode1. Sem ter que continuar ligando Mode.mode1.toString().

Larry
fonte

Respostas:

646

Você não pode. Eu acho que você tem quatro opções aqui. Todos os quatro oferecem uma solução, mas com uma abordagem ligeiramente diferente ...

Opção um: use o built-in name()em uma enumeração. Isso é perfeitamente bom se você não precisar de nenhum formato de nomeação especial.

    String name = Modes.mode1.name(); // Returns the name of this enum constant, exactly as declared in its enum declaration.

Opção 2: adicione propriedades de substituição às suas enumerações, se quiser mais controle

public enum Modes {
    mode1 ("Fancy Mode 1"),
    mode2 ("Fancy Mode 2"),
    mode3 ("Fancy Mode 3");

    private final String name;       

    private Modes(String s) {
        name = s;
    }

    public boolean equalsName(String otherName) {
        // (otherName == null) check is not needed because name.equals(null) returns false 
        return name.equals(otherName);
    }

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

Opção três: use finais estáticos em vez de enumerações:

public final class Modes {

    public static final String MODE_1 = "Fancy Mode 1";
    public static final String MODE_2 = "Fancy Mode 2";
    public static final String MODE_3 = "Fancy Mode 3";

    private Modes() { }
}

Opção quatro: as interfaces têm todos os campos públicos, estáticos e finais:

public interface Modes {

    String MODE_1 = "Fancy Mode 1";
    String MODE_2 = "Fancy Mode 2";
    String MODE_3 = "Fancy Mode 3";  
}
Michael J. Lee
fonte
46
Esta resposta é de fato errado: como você pode chamar .name()See: stackoverflow.com/a/6667365/887836
Alex
3
@ kato2 não está correto. O método .name () é criado automaticamente pelo compilador
Sean Patrick Floyd
10
JavaDoc: String java.lang.Enum.name () Retorna o nome dessa constante enum, exatamente como declarado em sua declaração enum. A maioria dos programadores deve usar o método toString em preferência a este, pois o método toString pode retornar um nome mais amigável. Esse método foi desenvolvido principalmente para uso em situações especializadas em que a correção depende da obtenção do nome exato, que não varia de versão para versão. Retorna: o nome desta constante enum
SuperRetro
O awser de @ Ryan Stewart requer código menos e menos código == menos chance de erros
Eric
4
Não use interfaces para manter constantes: Java efetivo (3ª edição) recomenda "usar interfaces apenas para definir tipos" (item 22).
Olivier Grégoire
481

Cada enumeração possui um método name () e um método valueOf (String). O primeiro retorna o nome da seqüência de caracteres da enumeração e o último fornece o valor da enumeração cujo nome é a sequência. É assim que você está procurando?

String name = Modes.mode1.name();
Modes mode = Modes.valueOf(name);

Há também um valor estáticoOf (Class, String) no próprio Enum, para que você também possa usar

Modes mode = Enum.valueOf(Modes.class, name);
Ryan Stewart
fonte
50
ISSO deve ser uma RESPOSTA! Usar algo como A ("A") pode ser fonte de erros e é um trabalho extra sem sentido!
Firzen
12
@Firzen não se for permitido que o valor da string contenha espaços ou hífens, como é o caso some-really-long-string.
ceving
@ceving A questão não é bem formulada em relação a espaços e hífens. As perguntas mostram um exemplo hifenizado, mas não pergunta como criar uma Enum usando valores hifenizados. Em vez disso, a pergunta pergunta como obter o valor String sem precisar chamar toString sem especificar valores hifenizados. Dito isso, acho que essa poderia ser uma resposta melhor se fosse alterada para mencionar que os valores Enum ainda devem seguir as regras de nomenclatura Java e precisariam usar algo mencionado na resposta aceita se esses caracteres fossem necessários.
Hazok 15/05
Eu queria adicionar um link para mkyong, que usa o valueOf(String)método em combinação com toUpperCase(Locale)para garantir a conversão de String.
Identificação desconhecida
2
A razão pela qual muitas pessoas preferem a abordagem de propriedade sobre Enum.name () é que a lógica baseada em Enum.name () fica para sempre à mercê dos nomes de valor. Se o código mudar no futuro, isso poderá se transformar em um problema não trivial, pois toda a lógica anterior interromperá as alterações no conjunto de valores do Enum. Substituir e usar a abordagem toString () permite que o desenvolvedor tenha maior controle sobre os valores em uso, incluindo duplicação, caracteres de nome de variável inválidos etc. Apenas não esqueça de substituir também valueOf ().
indivisível
79

Você pode substituir o toString()método para cada valor de enumeração.

Exemplo:

public enum Country {

  DE {
    @Override
    public String toString() {
      return "Germany";
    }
  },
  IT {
    @Override
    public String toString() {
      return "Italy";
    }
  },
  US {
    @Override
    public String toString() {
      return "United States";
    }
  }

}

Uso:

public static void main(String[] args) {
  System.out.println(Country.DE); // Germany
  System.out.println(Country.IT); // Italy
  System.out.println(Country.US); // United States
}
Benny Neugebauer
fonte
2
Eu gosto disso. Não há razão para não usar um enum como uma classe com mais funcionalidades, como obter uma lista de todos os valores, obter uma corda de cada tipo, etc.
diegosasw
8
Feio e não reutilizável. É muito melhor fornecer um valor de sequência como construtor para Country e substituir o método toString () da enumeração.
greg7gkb
4
Essa é uma boa técnica quando você possui uma enumeração bastante grande e deseja substituir apenas o que as coisas imprimem como para um ou dois membros.
Donal Fellows
3
Isso não é escalável. Não faça isso.
sebnukem
11
Isso faz sentido para mim
hanzolo
35

Como Benny Neugebauer menciona, você pode sobrescrever o toString (). No entanto, em vez de sobrescrever o toString para cada campo de enum, eu gosto mais de algo assim:

public enum Country{
    SPAIN("España"),
    ITALY("Italia"),
    PORTUGAL("Portugal");


    private String value;

    Country(final String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    @Override
    public String toString() {
        return this.getValue();
    }
}

Você também pode adicionar um método estático para recuperar todos os campos, imprimi-los, etc. Simplesmente chame getValue para obter a sequência associada a cada item do Enum

diegosasw
fonte
30

mode1.name()ou String.valueOf(mode1). Não fica melhor que isso, receio

Sean Patrick Floyd
fonte
21
public enum Modes {
  MODE1("Mode1"),
  MODE2("Mode2"),
  MODE3("Mode3");

 private String value;
 public String getValue() {
    return value;
   }
 private Modes(String value) {
  this.value = value;
 } 
}

você pode fazer uma ligação como abaixo, sempre que quiser obter o valor como uma sequência da enumeração.

Modes.MODE1.getvalue();

Isso retornará "Mode1" como uma String.

vamsi
fonte
6

Você pode usar Mode.mode1.name()no entanto, muitas vezes não precisa fazer isso.

Mode mode =
System.out.println("The mode is "+mode);
Peter Lawrey
fonte
6
Vale a pena notar que o operador + chamará toString () na enumeração e não name (). E toString () pode ser substituída para retornar algo diferente do nome (mesmo que não é desejável)
JB Nizet
11
Ambos name()e toString()podem ser substituídos, mas espero que isso fique claro ao ler o código para o enumse isso estiver acontecendo.
Peter Lawrey
9
No. name () é final e sempre retorna o nome da enum, conforme declarado em sua declaração de enum.
JB Nizet
11
@JB Nizet, você está certo. name()é final. Obrigado por me corrigir. :)
Peter Lawrey
6

Até onde eu sei, a única maneira de obter o nome seria

Mode.mode1.name();

Se você realmente precisar dessa maneira, no entanto, poderá fazer:

public enum Modes {
    mode1 ("Mode1"),
    mode2 ("Mode2"),
    mode3 ("Mode3");

    private String name;       

    private Modes(String s) {
        name = s;
    }
}
Jake Roussel
fonte
11
Mas, neste caso, Mode.mode1ainda não é do tipo String.
Larry
Oh, certo. Você precisaria de um método getName (), que derruba o propósito, então não, você não pode fazer isso.
Jake Roussel
"nome" é um nome de campo inválido, é um campo padrão de enum.
Wooff 10/07/2015
6

Para meus enums, não gosto de pensar neles sendo alocados com 1 String cada. É assim que eu implemento um método toString () em enumerações.

enum Animal
{
    DOG, CAT, BIRD;
    public String toString(){
        switch (this) {
            case DOG: return "Dog";
            case CAT: return "Cat";
            case BIRD: return "Bird";
        }
        return null;
    }
}
Ethan
fonte
11
Lançar uma exceção de tempo de execução seria melhor em vez de retornar nulo, pois deveria ser um código inacessível?
aluga
O returné redundante, porque a ativação de enumerações com todas as enumerações está terminando.
Danon
5

Você pode simplesmente usar:

""+ Modes.mode1
camarada
fonte
Não tenho 100% de certeza disso, mas, tanto quanto meu conhecimento é, esse elenco não é necessário, é? Concatenar uma string vazia com outra variável deve solicitar automaticamente uma conversão, ou há alguma exceção a essa regra?
ID desconhecido
11
Você está certo, a versão correta deve estar "" + Modes.mode1. Eu corrigi a resposta
Buddy
A resposta de EB também se beneficia por ser uma reminiscência do idioma python ''.join()
antrópico android
5

minha solução para o seu problema!

import java.util.HashMap;
import java.util.Map;

public enum MapEnumSample {
    Mustang("One of the fastest cars in the world!"), 
    Mercedes("One of the most beautiful cars in the world!"), 
    Ferrari("Ferrari or Mercedes, which one is the best?");

    private final String description;
    private static Map<String, String> enumMap;

    private MapEnumSample(String description) {
        this.description = description;
    }

    public String getEnumValue() {
        return description;
    }

    public static String getEnumKey(String name) {
        if (enumMap == null) {
            initializeMap();
        }
        return enumMap.get(name);
    }

    private static Map<String, String> initializeMap() {
        enumMap = new HashMap<String, String>();
        for (MapEnumSample access : MapEnumSample.values()) {
            enumMap.put(access.getEnumValue(), access.toString());
        }
        return enumMap;
    }

    public static void main(String[] args) {

        // getting value from Description
        System.out.println(MapEnumSample.getEnumKey("One of the fastest cars in the world!"));

        // getting value from Constant
        System.out.println(MapEnumSample.Mustang.getEnumValue());

        System.out.println(MapEnumSample.getEnumKey("One of the most beautiful cars in the world!"));
        System.out.println(MapEnumSample.Mercedes.getEnumValue());

        // doesnt exist in Enum
        System.out.println("Mustang or Mercedes, which one is the best?");
        System.out.println(MapEnumSample.getEnumKey("Mustang or Mercedes, which one is the best?") == null ? "I don't know!" : "I believe that "
                + MapEnumSample.getEnumKey("Ferrari or Mustang, which one is the best?") + " is the best!.");

        // exists in Enum
        System.out.println("Ferrari or Mercedes, wich one is the best?");
        System.out.println(MapEnumSample.getEnumKey("Ferrari or Mercedes, which one is the best?") == null ? "I don't know!" : "I believe that "
                + MapEnumSample.getEnumKey("Ferrari or Mercedes, which one is the best?") + " is the best!");

    }
}
Renan Galdino da Silva
fonte
Parece que o problema do OP foi resolvido há 4 anos, mas bem-vindo ao StackOverflow :)
TDG
11
Graças, apenas para ajudar alguém como eu, procurando respostas mais objetivas para velhos problemas :)
Renan Galdino da Silva
3

Enum é apenas uma classe especial. Enums podem armazenar campos adicionais, implementar métodos etc. Por exemplo

public enum Modes {
    mode1('a'),
    mode2('b'),
    mode3('c'),
    ;
    char c;

    private Modes(char c) {
        this.c = c;
    }
    public char character() {
        return c;
    }
}

Agora você pode dizer:

System.out.println(Modes.mode1.character())

e veja a saída: a

AlexR
fonte
3
package com.common.test;

public  enum Days {


    monday(1,"Monday"),tuesday(2,"Tuesday"),wednesday(3,"Wednesday"),
    thrusday(4,"Thrusday"),friday(5,"Friday"),saturday(6,"Saturday"),sunday(7,"Sunday");

    private int id;
    private String desc;


    Days(int id,String desc){
        this.id=id;
        this.desc=desc;
    }

    public static String getDay(int id){

        for (Days day : Days.values()) {
            if (day.getId() == id) {
                return day.getDesc();
            }
        }
        return null;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }



};

fonte
11
Você pode explicar como isso resolveria o problema?
Phani
você pode chamar esse enum em qualquer lugar usando esta linha: int id = 1; String dayName = Days.getDay (id); , passe aqui id. retornará a descrição para esse ID que é "terça-feira"
2

Este método deve funcionar com qualquer enum:

public enum MyEnum {
    VALUE1,
    VALUE2,
    VALUE3;

    public int getValue() {
        return this.ordinal();
    }

    public static DataType forValue(int value) {
        return values()[value];
    }

    public String toString() {
        return forValue(getValue()).name();
    }
}
Caner
fonte
2
public enum Environment
{
    PROD("https://prod.domain.com:1088/"),
    SIT("https://sit.domain.com:2019/"),
    CIT("https://cit.domain.com:8080/"),
    DEV("https://dev.domain.com:21323/");

    private String url;

    Environment(String envUrl) {
        this.url = envUrl;
    }

    public String getUrl() {
        return url;
    }
}

String prodUrl = Environment.PROD.getUrl();

Irá imprimir:

https://prod.domain.com:1088/

Esse design para constantes de seqüência de caracteres enum funciona na maioria dos casos.

lokesh
fonte
0

depois de muitas tentativas eu vim com esta solução

public static enum Operation {

    Addition, Subtraction, Multiplication, Division,;

    public String getUserFriendlyString() {
        if (this==Addition) {
            return " + ";
        } else if (this==Subtraction) {
            return " - ";
        } else if (this==Multiplication) {
            return " * ";
        } else if (this==Division) {
            return " / ";
        }
        return "undefined";
       }
}
Basheer AL-MOMANI
fonte
0

Você pode tentar isso:

public enum Modes {
    some-really-long-string,
    mode1,
    mode2,
    mode3;

    public String toString(){
        switch(this) {
            case some-really-long-string:
                return "some-really-long-string";
            case mode2:
                return "mode2";
            default: return "undefined";
        }
    }

}

Kamil Naja
fonte
0

Eu encontrei este é mais fácil para evitar erro de tipo:

public enum Modes {
    some-really-long-string,
    mode1,
    mode2,
    mode3;

    String str;

    Modes(){
        this.str = super.name();
    }

    @Override
    @NonNull
    public String toString() {
        return str;
    }

no entanto - isso pode funcionar quando você precisar usar uma String em um log / println ou sempre que o Java compilar o método toString () automaticamente, mas em uma linha de código como esta ->

// sample method that require (string,value)
intent.putExtra(Modes.mode1 ,shareElement.getMode()); // java error
// first argument enum does not return value

em vez disso, como mencionado acima, você ainda precisará estender a enumeração e usá-la .name() em casos como este:

intent.putExtra(Modes.mode1.name() ,shareElement.getMode()); 
ItzikH
fonte