2010-11-16 20 views
41

Tengo un problema en Java al utilizar Enums. He leído la documentación sobre la asignación de parámetros de valor a Enums. Pero, mi pregunta es ¿qué pasa con los valores múltiples, es posible?Java enum valueOf() con valores múltiples?

Esto lo que me gustaría lograr: Tengo un Enum para idiomas. Cada lengua está representada por su nombre y algunos alias cortos (no siempre, y no siempre el mismo número de alias)

He aquí un ejemplo:

public enum Language{ 
English("english", "eng", "en", "en_GB", "en_US"), 
German("german", "de", "ge"), 
Croatian("croatian", "hr", "cro"), 
Russian("russian") 
} 

¿Puedo definir una enumeración como esto y conseguir los valores enum correctos al llamar a Language.valueOf() ???

+0

I 'd recomendamos que vea mis artículos: http://java.dzone.com/articles/enum-tricks-customized-valueof y http://java.dzone.com/articles/enum-tricks-hierarchical-data Aunque no estoy seguro de entender exactamente qué es lo que realmente quiere, creo que al menos uno de estos artículos contiene la respuesta a su pregunta. – AlexR

Respuesta

59

Esto es probablemente similar a lo que está tratando de lograr.

public enum Language{ 
    English("english", "eng", "en", "en_GB", "en_US"), 
    German("german", "de", "ge"), 
    Croatian("croatian", "hr", "cro"), 
    Russian("russian"); 

    private final List<String> values; 

    Language(String ...values) { 
     this.values = Arrays.asList(values); 
    } 

    public List<String> getValues() { 
     return values; 
    } 
} 

Remember enums es una clase como las otras; English("english", "eng", "en", "en_GB", "en_US") está llamando al constructor enum.

Puede recuperar el valor enum correspondiente a una cadena a través de un método de búsqueda (puede volver a ponerlo como método estático en la enumeración).

public static Language find(String name) { 
    for (Language lang : Language.values()) { 
     if (lang.getValues().contains(name)) { 
      return lang; 
     } 
    } 
    return null; 
} 
+2

Podría estar malinterpretando esto, pero creo que él quiere poder hacer Language.valueOf ("eng") o Language.valueOf ("english"), y recuperar la misma instancia enum. – Andy

+0

Ok, eso no es posible con el método valueOf defaul; Edité la respuesta para incluir un método para obtener ese efecto. – Flavio

+0

Sí, yo también estaba confundido, realmente es una idea inteligente, creo, pero no posible. – Andy

3

En resumen, no.

El parámetro para el método valueOf() debe ser solo la Cadena del tipo de constante enum. Por lo tanto, no puede variar, o buscar posibles valores. Vea el JavaDoc.

Debe escribir su propio método de utilidad para devolver el tipo de enumeración adecuado para los valores dados.

1

Guau, respuesta muy rápida :) Gracias chicos.

Andy tiene razón. Quiero llamar a Language.valueOf ("eng") o Language.valueOf ("english") y obtener Language.English como return.

Ya tengo una función de utilidad que hace esto, pero no es muy agradable. Función de cambio que verifica los valores de cadena y devuelve la instancia enum apropiada.

Pero hay una gran cantidad de reescritura de código (tengo cca 30-40 idiomas). Y si deseo agregar un idioma, tengo que agregarlo a enum e implementar una nueva verificación en la clase de utilidad.

Voy a probar el enfoque Flavios. Solo una pregunta. Su constructor, ¿no debería ser?

Language(List<String> values) { 
    this.values = Arrays.asList(values); 
} 
+0

Si usa su tipo de constructor (debe eliminar la llamada 'Arrays.asList' para compilarlo), las instancias tendrían que crearse con 'inglés (Arrays.asList (" inglés "," eng "," en "," en_GB "," en_US "))'. También puede almacenar los valores como una matriz simple en lugar de una lista ... sin embargo, la búsqueda sería un poco más incómoda. – Flavio

2

normalizar los datos:

public enum LanguageCode 
{ 
    ENGLISH, 
    GERMAN, 
    CROATIAN, 
    RUSSIAN, 
    // ... 
} 
// (add whatever initialization you want to that 

Entonces

public enum Language{ 
    english(ENGLISH), 
    eng(ENGLISH), 
    en(ENGLISH), 
    en_GB(ENGLISH), 
    // ... 
} 

etc.

+1

Hmmm ... No creo que eso funcione. Los miembros de LanguageEnum serían entonces inglés, eng, en, en_GB ... y no INGLÉS, ALEMÁN ... – zolakt

10

Asignación de una cadena a un valor de enumeración, es normalmente lo que el método estático valueOf está haciendo por tú.Entonces, si quieres lograr esto con el uso de sinónimos, tendrás que desarrollar algo similar. Sin embargo, no queremos dar la impresión de que podemos anular un método estático, por lo que simplemente lo nombramos diferente para este fin: fromString debería ser apropiado.

public enum Language { 
    ENGLISH("eng", "en", "en_GB", "en_US"), 
    GERMAN("de", "ge"), 
    CROATIAN("hr", "cro"), 
    RUSSIAN("ru"), 
    BELGIAN("be",";-)"); 

    static final private Map<String,Language> ALIAS_MAP = new HashMap<String,Language>(); 
    static { 
    for (Language language:Language.values()) { 
     // ignoring the case by normalizing to uppercase 
     ALIAS_MAP.put(language.name().toUpperCase(),language); 
     for (String alias:language.aliases) 
     ALIAS_MAP.put(alias.toUpperCase(),language); 
    } 
    } 

    static public boolean has(String value) { 
    // ignoring the case by normalizing to uppercase 
    return ALIAS_MAP.containsKey(value.toUpperCase()); 
    } 

    static public Language fromString(String value) { 
    if (value == null) throw new NullPointerException("alias null"); 
    Language language = ALIAS_MAP.get(value); 
    if (language == null) 
     throw new IllegalArgumentException("Not an alias: "+value); 
    return language; 
    } 

    private List<String> aliases; 
    private Language(String... aliases) { 
    this.aliases = Arrays.asList(aliases); 
    } 
} 

Como ventaja de este tipo de aplicación se puede, como se ha demostrado, también implementar fácilmente el método estático has para probar si un alias dado es parte del conjunto de valor de enumeración. Al mismo tiempo, hemos aplicado algunas buenas convenciones de nomenclatura:

  • la enumeración valores van en mayúsculas, para indicar que en realidad son estáticas (instancias finales Singleton).
  • al mismo tiempo, también colocamos todas las demás terminaciones estáticas en mayúsculas.

Tenga en cuenta que no tenemos que repetir el nombre del valor de enumeración en sí: siempre tenemos en cuenta su propio nombre automáticamente (se agrega a la ALIAS_MAP), y encima se normaliza todo a mayúsculas para que sea sensible a mayúsculas .

Parece grande, pero durante el uso de la enumeración, se ve bastante:

public void main() { 
    Language myLanguage = Language.fromString("en_GB"); 
    if (myLanguage == Language.ENGLISH) { 
    System.out.println("Yes, I know, you understand English!"); 
    } 
} 

El contenedor de respaldo para los alias es una Map, un HashMap Para ser más específicos. El HashMap proporciona una ruta de acceso rápido a los alias, y también es fácil de cultivar. Cada vez que pensamos en 'indexar' algo, probablemente un HashMap debería ser nuestra primera opción.

Tenga en cuenta que para un uso transparente, almacenamos el nombre de la constante enum (recuperada a través del método name()) y todos los alias. Podríamos haber implementado esto de manera diferente al primero intentar hacer la búsqueda utilizando el método estático valueOf incorporado. Es una opción de diseño, pero que potencialmente podría tener que lidiar con excepciones adicionales, etc.

0

Alternativamente añadir un método, por lo que sus enumeraciones y constructor puede ser más limpio, si la estructura es más complicada:

public Language getLanguage(String code){ 
    switch(code){ 
    case "en": 
    case "en_GB": 
    ... 
    case "eng": 
     return ENGLISH; 
    case "rus": 
    case "russian": 
     return RUSSIAN; 
    } 
}