¿La mejor manera de crear una enumeración de cadenas?


364

¿Cuál es la mejor manera de hacer que un enumtipo represente un conjunto de cadenas?

Intenté esto:

enum Strings{
   STRING_ONE("ONE"), STRING_TWO("TWO")
}

¿Cómo puedo usarlos como Strings?

Respuestas:


605

No sé qué quieres hacer, pero así es como traduje tu código de ejemplo ...

package test;

/**
 * @author The Elite Gentleman
 *
 */
public enum Strings {
    STRING_ONE("ONE"),
    STRING_TWO("TWO")
    ;

    private final String text;

    /**
     * @param text
     */
    Strings(final String text) {
        this.text = text;
    }

    /* (non-Javadoc)
     * @see java.lang.Enum#toString()
     */
    @Override
    public String toString() {
        return text;
    }
}

Alternativamente, puede crear un método getter para text.

Ahora puedes hacer Strings.STRING_ONE.toString();


3
No sé si es un requisito del compilador, pero private String textdebería ser definitivo.
Jonathan

77
@ Tomás Narros Sí, aún puede asignarlo en el constructor, siempre que no le dé un valor cuando lo declare final.
Jonathan

28
@The Elite Gentleman Sería malo si el valor de una constante enum cambiara durante el tiempo de ejecución, por lo que incluso si no fuera necesario, finalsería lo mejor.
Jonathan

8
No olvide que las enumeraciones no se pueden construir newcomo lo es el constructor private. Esencialmente, la creación de objetos está prohibida y finalno es realmente necesaria en este caso.
Buhake Sindi

66
@BuhakeSindi: eso es cierto, pero accidentalmente uno puede estar inclinado a poner un setText(String)enum y eso puede desatar el infierno :) finaltipo de documentos de su intención de que sea una constante con el soporte del compilador. Si usaras Stringconstantes, no lo declararías como public static String, ¿verdad?
TWiStErRob

104

Valores de cadena personalizados para Enum

de http://javahowto.blogspot.com/2006/10/custom-string-values-for-enum.html

El valor de cadena predeterminado para java enum es su valor nominal o el nombre del elemento. Sin embargo, puede personalizar el valor de la cadena anulando el método toString (). Por ejemplo,

public enum MyType {
  ONE {
      public String toString() {
          return "this is one";
      }
  },

  TWO {
      public String toString() {
          return "this is two";
      }
  }
}

Ejecutar el siguiente código de prueba producirá esto:

public class EnumTest {
  public static void main(String[] args) {
      System.out.println(MyType.ONE);
      System.out.println(MyType.TWO);
  }
}


this is one
this is two

16
Esta no es una manera eficiente de hacer esto. Esto crea una nueva clase personalizada para cada valor en la enumeración. En el ejemplo anterior, verá lo siguiente en el bindirectorio: EnumTest $ MyType.class EnumTest $ MyType $ 1.class EnumTest $ MyType $ 2.class que se sumará muy rápido. Es mejor hacerlo como la respuesta esperada, pasando valores al constructor enum. En realidad no estoy de acuerdo con anular toString(); Creo que es mejor usar un getter explícito, getKey()ya que la anulación toString()puede ser inesperada por otro usuario de la enumeración.
Matt Quigley

totalmente de acuerdo con @MattQuigley. Al hacerlo, aliente a los usuarios a usar toString para cosas para las que no debe usarse. Si necesita una etiqueta, preferiría agregar un atributo de etiqueta
Sebastien Lorber, el

Además, no hay forma de ir al revés (desde una cadena hasta el objeto enum) que probablemente será necesario en algún momento.
Adrian Smith el

anular toString no afecta valueOf (), ¿verdad?
Dmitriusan

69

Utiliza su name()método:

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

enum Strings {
    ONE, TWO, THREE
}

rendimientos ONE.


17
Sí, pero Strings.STRING_ONE.name()produce "STRING_ONE", no "ONE". Esto simplemente no es una buena respuesta. No puede tener ninguna cadena que no sea un identificador de Java válido, etc.
Mark Peters el

2
@ Mark, es cierto, no puede manejar ningún personaje. Si el OP solo quiere un solo personaje, esta solución es más sencilla que la sugerencia de The Elite Gentleman. Pero, de hecho: si el rango de caracteres excede los que puede tener un identificador de Java válido, esto es una opción prohibida. Buen punto.
Bart Kiers

Es muy razonable tener una convención de nomenclatura interna para una enumeración que sea diferente de lo que uno quisiera mostrar con toString () (especialmente si un usuario ve la salida), por lo que no creo que esto sea exactamente lo que era el OP buscando.
Michael McGowan

17
El resultado de name()puede verse afectado por un programa ofuscador. Me encontré con un problema similar hace un tiempo. Por ejemplo, con Proguard necesita evitar esto. Ver clases de tratamiento de enumeración
noisecapella

Esto es excelente. Funciona con valores como: var_name, var_superlong_but_not_toooooo_long_name, incluso etc. (sin los puntos triples)

19

Establezca el nombre de enumeración para que sea el mismo que la cadena que desea o, más generalmente, puede asociar atributos arbitrarios con sus valores de enumeración:

enum Strings {
   STRING_ONE("ONE"), STRING_TWO("TWO");
   private final String stringValue;
   Strings(final String s) { stringValue = s; }
   public String toString() { return stringValue; }
   // further methods, attributes, etc.
}

Es importante tener las constantes en la parte superior y los métodos / atributos en la parte inferior.


Y también tener un constructor privado .
Buhake Sindi

1
los constructores enum son privados por defecto y no requieren un modificador de acceso. Pero ese es un buen punto sobre los modificadores de acceso en general, he actualizado mi código para agregarlos al atributo y al descriptor de acceso.
Adrian Smith

15

Dependiendo de lo que quiera decir con "utilizarlos como cadenas", es posible que no desee utilizar una enumeración aquí. En la mayoría de los casos, la solución propuesta por The Elite Gentleman le permitirá usarlos a través de sus métodos toString, por ejemplo, en System.out.println(STRING_ONE)o String s = "Hello "+STRING_TWO, pero cuando realmente necesite cadenas (por ejemplo STRING_ONE.toLowerCase()), puede preferir definirlas como constantes:

public interface Strings{
  public static final String STRING_ONE = "ONE";
  public static final String STRING_TWO = "TWO";      
}

55
en realidad esto es lo que estoy tratando de evitar ...!
Dori

En realidad, si también quieren toLowerCase()mi solución, pueden irse Strings.STRING_TWO.toString().toLowerCase().
Buhake Sindi

Claro, pero eso no es usarlos como cadenas como lo interpreté. Como Rrackham no parece requerir ese uso, por supuesto debería usar la solución de enumeración propuesta.
hd42

66
No debe usar interfaceen lugar de una finalclase con privateconstructor. Es una práctica desanimada.
Aleks N.

1
@mils, las soluciones que usan enumeraciones funcionan igual de bien en un interruptor. Sugeriría esta solución solo si se necesitan cadenas directamente.
hd42

8

Puedes usar eso para la cadena Enum

public enum EnumTest {
    NAME_ONE("Name 1"),
    NAME_TWO("Name 2");

    private final String name;

    /**
     * @param name
     */
    private EnumTest(final String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

Y llamar desde el método principal

public class Test {
    public static void main (String args[]){
        System.out.println(EnumTest.NAME_ONE.getName());
        System.out.println(EnumTest.NAME_TWO.getName());
    }
}

5

Si no desea utilizar constructores , y desea tener un nombre especial para el método, intente esto:

public enum MyType {
  ONE {
      public String getDescription() {
          return "this is one";
      }
  },    
  TWO {
      public String getDescription() {
          return "this is two";
      }
  };

  public abstract String getDescription();
}

Sospecho que esta es la solución más rápida . No hay necesidad de usar variables finales .


pero con este todavía tiene que llamar a getDescription () de los cuales la persona que pregunta quiere llamar a ONE o acceder a él como una constante.
kinsley kajiva

Prefiero esto. ¿Qué sucederá si necesita aceptar más información? Con esta solución solo agregue métodos abstractos protegidos y anúlelo. Además, no es necesario anular el método toString (). Está limpio y se puede aceptar como la mejor respuesta.
Arundev

0

Obtener y establecer con valores predeterminados.

public enum Status {

    STATUS_A("Status A"),  STATUS_B("Status B"),

    private String status;

    Status(String status) {
        this.status = status;
    }

    public String getStatus() {
        return status;
    }
}
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.