Pregunta Recuperar una enumeración usando 'valueOf' arroja RuntimeException - ¿Qué usar en su lugar?


Tengo la siguiente enumeración

enum Animal implements Mammal {
   CAT, DOG;

   public static Mammal findMammal(final String type) {
      for (Animal a : Animal.values()) {
         if (a.name().equals(type)) {
            return a;
         }
      }
   }
}

Originalmente había usado el Enum.valueOf(Animal.class, "DOG"); para encontrar un animal en particular Sin embargo, no sabía que si no se encuentra una coincidencia, IllegalArgumentException es aventado. Pensé que tal vez se devolvió un nulo. Entonces esto me da un problema. No quiero ver esto IllegalArgumentException si no se encuentra una coincidencia. Quiero poder buscar todas las enumeraciones de tipo Mammal y no quiero tener que implementar esto estático 'findMammal'para cada enum de tipo Mammal. Entonces mi pregunta es, ¿cuál sería la decisión de diseño más auspiciosa para implementar este comportamiento? Tendré un código de llamada como este:

public class Foo {
   public Mammal bar(final String arg) {
      Mammal m = null;
      if (arg.equals("SomeValue")) {
         m = Animal.findMammal("CAT");
      } else if (arg.equals("AnotherValue") {
         m = Human.findMammal("BILL");
      }
      // ... etc
   }
}

Como puede ver, tengo diferentes tipos de mamíferos: 'Animal', 'Humano', que son enumeraciones. No quiero tener que implementar 'findMammal' para cada Mamífero enum. Supongo que la mejor opción es simplemente crear una clase de utilidad que tome un argumento de Mamífero y lo busque. Tal vez hay una solución más ordenada.


11
2018-02-21 19:55


origen


Respuestas:


¿Qué tal crear una HashMap<String, Mammal>? Solo necesitas hacerlo una vez ...

public class Foo {

  private static final Map<String, Mammal> NAME_TO_MAMMAL_MAP;

  static {
    NAME_TO_MAMMAL_MAP = new HashMap<String, Mammal>();
    for (Human human : EnumSet.allOf(Human.class)) {
      NAME_TO_MAMMAL_MAP.put(human.name(), human);
    }
    for (Animal animal : EnumSet.allOf(Animal.class)) {
      NAME_TO_MAMMAL_MAP.put(animal.name(), animal);
    }
  }

  public static Mammal bar(final String arg) {
    return NAME_TO_MAMMAL_MAP.get(arg);
  }
}

Notas:

  • Esto regresará null si el nombre no existe
  • Esto no detectará una colisión de nombre
  • Es posible que desee utilizar un mapa inmutable de alguna descripción (por ejemplo, a través de Guayaba)
  • Es posible que desee escribir un método de utilidad para crear un mapa inmutable de nombre a valor para una enumeración general, luego simplemente combine los mapas :)

12
2018-02-21 20:03



Si no quiere tocar cada Enum (comprensible), un método de utilidad es el camino a seguir. Entiendo que no quiero atrapar la excepción en tu código, pero atraparlo en el método debería estar bien:

public static <T extends Enum<T>> T findEnumValue(Class<T> type, String name) {
    if (name == null)
        return null;
    try {
        return Enum.valueOf(type, name.toUpperCase());
    } catch (IllegalArgumentException iae) {
        return null;
    }
}

9
2018-02-21 20:25



Puedes usar el getEnum Método vom the apache EnumUtils biblioteca. Devuelve la enumeración o null si no se encuentra

EnumUtils.getEnum(Animal.class, "CAT");

8
2017-10-14 08:34



Puedes usar la biblioteca de guayaba Enums clase permite esto:

Enums.valueOfFunction(MyEnum.class).apply(id);

7
2018-04-04 06:21



Usted tiene un error en su código. Si necesita que su función devuelva nulo cuando no encuentra algo, simplemente devuelva:

enum Animal implements Mammal {
   CAT, DOG;

   public static Mammal findMammal(final String type) {
      for (Animal a : Animal.values()) {
         if (a.name().equals(type)) {
            return a;
         }
      }
      return null; // this line works if nothing is found.
   }
}

Ninguna tecnología te ayudará si cometes el mismo error y te olvidas de crear algún valor de retorno para cada caso posible.

Su elección: Enum - EnumMaps - HashMap depende de la dinámica de sus datos. Las constantes de los enum no se pueden crear en tiempo de ejecución. Por otro lado, no necesita verificar muchas cosas en tiempo de ejecución, el compilador lo hará. Y es muy probable que te olvides de verificar algo. Lo que maneja fácilmente Jon Skeet podría darte un dolor de cabeza.


1
2018-02-24 08:43



No entiendo la pregunta, pero también necesitaba una valueOf eso regresa null en lugar de lanzar una excepción, entonces creé este método de utilidad:

public static <T extends Enum<T>> T valueOf(T[] values, String name)
{
    for (T value : values) {
        if (value.name().equals(name)) {
            return value;
        }
    }
    return null;
}

Podrías llamarlo así:

Animal a = valueOf(Animal.values(), "CAT");

1
2018-06-19 22:43



Si puedes decir

m = Animal.findMammal("CAT");

Por qué no puedes decir

m = Animal.CAT;

El "CAT" tendrá que coincidir con el nombre de las enumeraciones de todos modos y en su Ejemplo, "CAT" no se deriva de un parámetro sino que está codificado.

ACTUALIZAR (quizás más al punto de la pregunta)

Es un poco elaborado, pero ...

public interface Mammal{

    public void doMammalStuff();

    public static interface Finder{
        public Mammal find(String name, Class<Enum<?>> enumType);
    }

    public static Finder FINDER = new Finder(){
        public Mammal find(String name, Class<Enum<?>> enumType) {
            return enumType.forName( name );
    };
    };
}

Entonces podrías llamar

Mammal m = Mammal.FINDER.find( "CAT", Animal.class );

Su implementación de find () puede parecer diferente (es decir, no lanzar una excepción).


0
2018-02-21 20:13