Usando valores Enum como literales de cadena

389

¿Cuál es la mejor manera de usar los valores almacenados en una enumeración como literales de cadena? Por ejemplo:

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

Luego, podría usar Mode.mode1para devolver su representación de cadena como mode1. Sin tener que seguir llamando Mode.mode1.toString().

Larry
fuente

Respuestas:

646

No puedes Creo que tienes CUATRO opciones aquí. Los cuatro ofrecen una solución pero con un enfoque ligeramente diferente ...

Opción uno: use el incorporado en name()una enumeración. Esto está perfectamente bien si no necesita ningún formato de nombre especial.

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

Opción dos: agregue propiedades sobresalientes a sus enumeraciones si desea más control

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

Opción tres: use finales estáticos en lugar de enumeraciones:

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

Opción cuatro: las interfaces tienen cada campo público, estático y final:

public interface Modes {

    String MODE_1 = "Fancy Mode 1";
    String MODE_2 = "Fancy Mode 2";
    String MODE_3 = "Fancy Mode 3";  
}
Michael J. Lee
fuente
46
De hecho, esta respuesta es incorrecta: como puede llamar .name()Ver: stackoverflow.com/a/6667365/887836
Alex
3
@ kato2 no es correcto. El compilador crea el método .name () automáticamente
Sean Patrick Floyd el
10
JavaDoc: String java.lang.Enum.name () Devuelve el nombre de esta constante enum, exactamente como se declara en su declaración enum. La mayoría de los programadores deben usar el método toString con preferencia a este, ya que el método toString puede devolver un nombre más fácil de usar. Este método está diseñado principalmente para su uso en situaciones especializadas donde la corrección depende de obtener el nombre exacto, que no variará de una versión a otra. Devuelve: el nombre de esta constante de enumeración
SuperRetro
La respuesta de @Ryan Stewart requiere menos código y menos código == menos posibilidad de errores
Eric
44
No utilice interfaces para mantener constantes: Java eficaz (3ª edición) recomienda "utilizar interfaces solo para definir tipos" (elemento 22).
Olivier Grégoire
481

Cada enumeración tiene un método name () y valueOf (String). El primero devuelve el nombre de cadena de la enumeración, y el último da el valor de enumeración cuyo nombre es la cadena. ¿Es esto lo que estás buscando?

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

También hay un valor estático Of (Class, String) en Enum, por lo que también puede usar

Modes mode = Enum.valueOf(Modes.class, name);
Ryan Stewart
fuente
50
¡Esto debería ser una RESPUESTA! ¡Usar algo como A ("A") puede ser fuente de errores y es un trabajo extra sin sentido!
Firzen
12
@Firzen no si el valor de la cadena puede contener espacios o guiones, como es el caso some-really-long-string.
ceving
@ceving La pregunta no está bien redactada con respecto a espacios y guiones. Las preguntas muestran un ejemplo con guiones, pero no preguntan cómo crear una enumeración usando valores con guiones. En cambio, la pregunta pregunta cómo obtener el valor de String sin tener que llamar a toString sin especificar valores con guiones son un requisito. Dicho esto, creo que esta podría ser una mejor respuesta si se modificara para mencionar que los valores de Enum todavía deben seguir las reglas de nomenclatura de Java y necesitarían usar algo mencionado en la respuesta aceptada si se requieren dichos caracteres.
Hazok
Quería agregar un enlace a mkyong que usa el valueOf(String)método en combinación con toUpperCase(Locale)para garantizar la conversión de cadenas.
Identificación desconocida
2
La razón por la que mucha gente prefiere el enfoque de propiedad sobre Enum.name () es que la lógica basada en Enum.name () está siempre a merced de los nombres de valores. Si el código cambia en el futuro, esto podría convertirse en un problema no trivial, ya que toda la lógica anterior se romperá con los cambios en el conjunto de valores de Enum. Anular y usar el enfoque toString () le permite al desarrollador tener un mayor control sobre los valores en uso, incluyendo duplicación, caracteres de nombre de variable no válidos, etc. Simplemente no olvide anular también valueOf ().
indivisible el
79

Puede anular el toString()método para cada valor de enumeración.

Ejemplo:

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
fuente
2
Me gusta esto. No hay razón para no utilizar una enumeración como una clase con más funcionalidades, como obtener una lista de todos los valores, obtener una cadena de cada tipo, etc.
diegosasw
8
Feo y no reutilizable. Es mucho mejor proporcionar un valor de cadena como constructor para Country y luego anular el método toString () para la enumeración.
greg7gkb
44
Esta es una buena técnica cuando tiene una enumeración bastante grande y solo desea anular lo que se imprime para uno o dos miembros.
Donal Fellows
3
Esto no escala en absoluto. No hagas esto.
sebnukem
1
Esto tiene sentido para mí
hanzolo
35

Como menciona Benny Neugebauer, puede sobrescribir toString (). Sin embargo, en lugar de sobrescribir toString para cada campo de enumeración, me gusta más algo como esto:

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

También puede agregar un método estático para recuperar todos los campos, imprimirlos todos, etc. Simplemente llame a getValue para obtener la cadena asociada a cada elemento Enum

diegosasw
fuente
30

mode1.name()o String.valueOf(mode1). No hay nada mejor que eso, me temo

Sean Patrick Floyd
fuente
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;
 } 
}

puede hacer una llamada como a continuación donde quiera obtener el valor como una cadena de la enumeración.

Modes.MODE1.getvalue();

Esto devolverá "Mode1" como una cadena.

vamsi
fuente
6

Puede usar Mode.mode1.name()sin embargo, a menudo no necesita hacer esto.

Mode mode =
System.out.println("The mode is "+mode);
Peter Lawrey
fuente
66
Vale la pena señalar que el operador + llamará a toString () en la enumeración, y no name (). Y toString () puede anularse para devolver algo distinto del nombre (incluso si no es deseable)
JB Nizet
1
Tanto name()y toString()puede ser anulado, pero espero que esto será evidente a partir de la lectura del código para el enumcaso de que esto está sucediendo.
Peter Lawrey
99
No. name () es final y siempre devuelve el nombre de la enumeración tal como se declara en su declaración de enumeración.
JB Nizet
1
@JB Nizet, tienes razón. name()es final. Gracias por corregirme. :)
Peter Lawrey
6

Que yo sepa, la única forma de obtener el nombre sería

Mode.mode1.name();

Sin embargo, si realmente lo necesita de esta manera, podría hacer:

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

    private String name;       

    private Modes(String s) {
        name = s;
    }
}
Jake Roussel
fuente
1
Pero en este caso, Mode.mode1todavía no es de tipo String.
Larry
Correcto. Necesitaría un método getName (), que frustra el propósito, así que no, no puede hacer esto.
Jake Roussel
"nombre" es un nombre de campo incorrecto, es un campo estándar de enumeración.
Wooff
6

Para mis enumeraciones, no me gusta pensar que se les asigne 1 Cadena cada una. Así es como implemento un método toString () en enumeraciones.

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
fuente
1
¿Lanzar una excepción de tiempo de ejecución sería mejor en lugar de devolver nulo ya que debería ser un código inalcanzable?
alquila
El returnes redundante porque el encendido de las enumeraciones con todas las enumeraciones está terminando.
Danon
5

Simplemente puede usar:

""+ Modes.mode1
compañero
fuente
No estoy 100% seguro de esto, pero hasta donde yo sé, este elenco no es necesario, ¿verdad? La concatenación de una cadena vacía con otra variable debería requerir automáticamente una conversión, ¿o hay alguna excepción a esta regla?
Identificación desconocida
1
Tienes razón, la versión correcta debería ser "" + Modes.mode1. Arreglé la respuesta
amigo, el
La respuesta de EB también se beneficia al recordar el idioma de Python ''.join()
android antrópico
5

¡mi solución para tu 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
fuente
Parece que el problema del OP se resolvió hace 4 años, pero bienvenido a StackOverflow :)
TDG
1
Gracias, solo por ayudar a alguien como yo, buscando respuestas más objetivas a viejos problemas :)
Renan Galdino da Silva
3

Enum es solo una clase un poco especial. Las enumeraciones pueden almacenar campos adicionales, implementar métodos, etc. Por ejemplo

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

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

Ahora puedes decir:

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

y ver salida: a

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



};

fuente
1
¿Pueden explicarme cómo resolvería el problema?
Phani
puede llamar a esta enumeración en cualquier lugar utilizando esta línea: int id = 1; String dayName = Days.getDay (id); , pasa aquí id. devolverá Descripción para esa identificación que es "martes"
2

Este método debería funcionar con cualquier 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
fuente
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();

Imprimirá:

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

Este diseño para constantes de cadena enum funciona en la mayoría de los casos.

lokesh
fuente
0

después de muchos intentos, he venido con esta solución

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
fuente
0

Puedes probar esto:

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
fuente
0

Encontré que este es más fácil para evitar errores 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;
    }

sin embargo, esto puede funcionar cuando necesita usar una cadena en un log / println o cuando java compila el método toString () automáticamente, pero en una línea 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

en cambio, como se mencionó anteriormente, aún tendrá que extender la enumeración y usarla .name() en casos como este:

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