2009-10-02 15 views
18

Estoy tratando de buscar contra un conjunto Enum, sabiendo que a menudo habrá un no coincidente que arroja una excepción: me gustaría verificar que el valor exista antes de realizar la búsqueda para evitar las excepciones Mi enumeración es como la siguiente:Verificar valores de enum válidos antes de usar enum

public enum Fruit { 
    APPLE("apple"), 
    ORANGE("orange"); 
    ; 
    private final String fruitname; 
    Fruit(String fruitname) { 
     this.fruitname = fruitname; 
    } 
    public String fruitname() {return fruitname;} 
} 

y quiero comprobar si, por ejemplo, "banana" es uno de mis valores de enumeración antes de intentar utilizar la enumeración correspondiente. Podría iterar a través de los valores permisibles comparando mi cadena a

Fruit.values()[i].fruitname 

pero me gustaría ser capaz de hacer algo así (pseduo de código):

if (Fruit.values().contains(myStringHere)) {... 

Es eso posible? ¿Debo usar algo completamente diferente (Arrays? Maps?)?

EDITAR: al final me he ido con la sugerencia de NawaMan, pero gracias a todos por toda la ayuda.

Respuesta

22

Realmente no conozco una solución incorporada. Por lo tanto, puede que tenga que escribirlo usted mismo como un método estático.

public enum Fruit { 
    ... 
    static public boolean isMember(String aName) { 
     Fruit[] aFruits = Fruit.values(); 
     for (Fruit aFruit : aFruits) 
      if (aFruit.fruitname.equals(aName)) 
       return true; 
     return false; 
    } 
    ... 
} 
+5

"values ​​()" crea una matriz clonada cada vez, por lo que es mejor no llamarla con demasiada frecuencia. Llámelo solo una vez y guarde en caché los resultados, o use "EnumSet.allOf (Fruit.class)". – dogbane

+1

En JDK 1.7 se arregló. En JDK 1.5 hay comentarios para arreglar esto más tarde. No sé lo que sucede en JDK 1.6. – alexsmail

+1

Tenga en cuenta que esta solución es lenta para muchos valores. Es mejor hacer algo como http://stackoverflow.com/a/2546726/260805 en su lugar. – Ztyx

7

Cuando hago esto, generalmente lo injerto en mi clase enum.

public enum Fruit { 
     APPLE("apple"), 
     ORANGE("orange"); 

    // Order of initialisation might need adjusting, I haven't tested it. 
    private static final Map<String, Fruit> lookup = new HashMap<String, Fruit>(); 
    private final String fruitname; 
    Fruit(String fruitname) { 
     this.fruitname = fruitname; 
     lookup.put(fruitname, Fruit); 
    } 
    public String fruitname() {return fruitname;} 

    public static Fruit fromFruitname(String fruitname) { 
     return lookup.get(fruitname); 
    } 
} 

Pero:

  • Para las pequeñas enumeraciones es probablemente más eficiente para desplazarse por la lista.

Por cierto:

  • En esta situación, habría ido con la convención y usado el nombre() ya que es el mismo que el nombre de encargo, excepto para el caso
  • Esta solución (arreglado fácilmente.) es más útil cuando lo que tiene que buscar es completamente diferente al valor del nombre().
+0

Tomé permiso para fijar el ejemplo, tener el mapa como estática. – KLE

+0

El orden de inicialización está bien, no se preocupe. – KLE

+0

Sí, la falta de estática era un error tipográfico. – Trejkaz

2

Estoy de acuerdo con su deseo de que no se cree ninguna excepción. Es bueno para el rendimiento (como excepción vale mil instrucciones para construir el seguimiento de la pila), y es lógico cuando dices que a menudo no se encuentra (por lo tanto, no es una condición excepcional) .


creo que el for loop mencionas es correcta, si usted tiene sólo unos pocos valores de enumeración. Probablemente tendrá el mejor rendimiento de todos. Pero entiendo que no lo quieres.


Se puede construir un mapa para encontrar su valores de enumeración, que evitaría la excepción y devolver la enumeración adecuada al mismo tiempo.

Actualización: Trejkaz ya publicó el código que hace esto.


También tenga en cuenta que a veces, en vez de volver null como el tipo de retorno cuando coincide con ninguna instancia, algunos enumeración tiene una instancia dedicada para que (lo llaman vacio, o NOT_FOUND por ejemplo). La ventaja es que todos los códigos de llamadas no tienen que lidiar con valores nulos, y arriesgan no NullPointerException. Si es necesario, puede haber un método booleano que diga isFound() (devuelve verdadero excepto para esa instancia).Y los códigos que realmente deberían diferenciar esos valores de otros todavía pueden, mientras que los que no les importa simplemente pasan la instancia sin conocimiento de este caso especial.

+2

Touche. Buena llamada con la "condición no excepcional". Estaba pensando que una excepción debería tratarse como tal. Si no es realmente una excepción, no debería ser una excepción. +1, @KLE. – Rap

5

Voy a ser el contrario aquí ... Creo que su primer impulso (lanzar una excepción) es lo correcto.

Si está comprobando dentro de la lógica de negocio en lugar de la interfaz de usuario, no habrá ningún comentario en ese nivel para el usuario. (Si no está registrando la IU, tenemos otros problemas). Por lo tanto, la forma correcta de manejarlo es lanzando una excepción.

Por supuesto, eso no significa que tenga que tener la burbuja de excepción hasta el nivel de la IU, lo que cortocircuita el resto de su lógica. Lo que suelo hacer es poner la asignación enum en su propio pequeño try-catch y manejar la excepción reasignando o cualquier otra solución elegante que hayas ideado.

En resumen ... usted estaba en el dinero con su primer pensamiento. Ir con eso Simplemente cambie su manejo de excepciones un poco diferente.

2

¿Quizás no deberías usar un Enum en absoluto? Si tiene que lidiar regularmente con valores que no están definidos en su Enum, quizás debería usar algo como HashMap < String, Fruit > Luego puede usar containsKey(), para averiguar si existe una clave en particular.

2

Solo por mencionar otra posibilidad que permitirá que su código de llamada no tenga que preocuparse por las excepciones o comprobaciones condicionales, es siempre devolver una fruta. Si no se encuentra la cadena, devuelva Fruit.UNKNOWN, por ejemplo.

Ejemplo:

public enum Fruit { 
    public Fruit getValueOf(String name) { 
     for (Fruit fruit : Fruit.values()) { 
      if (fruit.fruitname.equals(name)) 
       return fruit; 
      } 
     } 
     return UNKNOWN; 
    } 
    ... 
} 
5

Así es como se puede hacer usando EnumSet.allOf para poblar un mapa:

public enum Fruit { 

    APPLE("apple"), 
    ORANGE("orange"); 

    private static final Map<String, Fruit> nameToValueMap = new HashMap<String, Fruit>(); 

    static { 
     for (Fruit value : EnumSet.allOf(Fruit.class)) { 
      nameToValueMap.put(value.name(), value); 
     } 
    } 

    private final String fruitname; 

    Fruit(String fruitname) { 
     this.fruitname = fruitname; 
    } 

    public String fruitname() { 
     return fruitname; 
    } 

    public static Fruit forName(String name) { 
     return nameToValueMap.get(name); 
    } 
} 
12

Hay un apache commons lang EnumUtils.isValidEnum(). Por desgracia, Bajo el capó, esto es usar la lógica try/catch y volviendo booleano, pero al menos su código se ve limpio:

if(EnumUtils.isValidEnum(Fruit.class, fruitname)) { .... 

Usted tendrá que utilizar la última biblioteca Commons-lang3 como commons-lang 2.x no tiene esta función

+0

también comprueba valores nulos, eso es suficiente. Gracias, tu comentario fue útil. –

+1

No sabía acerca de esta función. Esta es realmente la manera más clara y fácil (si tiene la dependencia) – Seega

+0

, no verifica el valor. p.ej. si uno tiene APPLE ("apple"), y la llamada es para apple, devolverá false. Si la llamada es para APPLE, devolverá true. – Dejell

3

Esta es mi solución. Creé un conjunto para que no tenga que especificar un constructor. Esto también tiene el beneficio adicional de que el valor que se busca tiene que coincidir con el caso de la enumeración.

public enum Fruit{ 
    Apple, 
    Orange; 

    private final static Set<String> values = new HashSet<String>(Fruit.values().length); 

    static{ 
     for(Fruit f: Fruit.values()) 
      values.add(f.name()); 
    } 

    public static boolean contains(String value){ 
     return values.contains(value); 
    } 

} 
2

En java8 que puede hacerlo de esta manera

public static boolean isValidFruit(final String fruit) { 
    return Arrays.stream(Fruit.values()) 
     .map(Fruit::name) 
     .collect(Collectors.toSet()) 
     .contains(fruit); 
}