Получение перечисления с помощью "valueOf" вызывает исключение RuntimeException-что использовать вместо этого?

у меня есть следующее перечисление

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

я первоначально использовал Enum.valueOf(Animal.class, "DOG"); найти конкретное животное. Однако я не знал, что если совпадение не найдено, то IllegalArgumentException бросается. Я подумал, что, возможно, null был возвращен. Так что у меня проблема. Я не хочу ловить это IllegalArgumentException если совпадение не найдено. Я хочу иметь возможность искать все перечисления типа Mammal и я не хочу реализовывать этот статический'findMammal' для каждого перечислимого типа Mammal. Итак, мой вопрос является ли, что было бы наиболее благоприятным проектным решением для реализации этого поведения? У меня будет такой код вызова:

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

как вы можете видеть, у меня есть разные типы млекопитающих - "животное", "человек", которые являются перечислениями. Я не хочу реализовывать "findMammal" для каждого перечисления млекопитающих. Я полагаю, что лучше всего просто создать класс утилиты, который принимает аргумент млекопитающего и ищет его? Может быть, есть более аккуратное решение.

7 ответов


как насчет создания HashMap<String, Mammal>? Вам нужно сделать это только один раз...

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

Примечания:

  • возвращает null Если имя не существует
  • это не обнаружит столкновения имен
  • вы можете использовать неизменяемую карту некоторого описания (например, via гуавы)
  • вы можете написать метод утилиты для создания неизменяемой карты "имя-значение" для общего перечисления, а затем просто объединить карты :)

Если вы не хотите касаться каждого перечисления (понятно), метод утилиты-это путь. Я понимаю, что не хочу ловить исключение в вашем коде, ловить его в методе должно быть хорошо, хотя:

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

можно использовать getEnum метод vom apache EnumUtils библиотека. Он возвращает перечисление или null если не нашли.

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

вы можете использовать библиотеку guava. метки класс позволяет это:

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

Я не понимаю вопроса, но мне также нужен valueOf возвращает null вместо того, чтобы выбрасывать исключение, поэтому я создал этот метод утилиты:

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

вы можете назвать это так:

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

у вас ошибка в коде. Если вам нужно, чтобы ваша функция возвращала null, когда она ничего не находит, просто верните ее:

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

никакая технология не поможет, если вы сделаете ту же ошибку и забудете создать некоторое возвращаемое значение для каждого возможного случая.

ваш выбор: Enum-EnumMaps-HashMap зависит от динамики ваших данных. Перечисления, константы не могут быть созданы во время выполнения. С другой стороны, вам не нужно проверять много вещей в runtime-compiler будут делать это. И вы очень вероятно забудете что-то проверить. То, что легко удается Джону скиту, может вызвать головную боль.


если вы можете сказать

m = Animal.findMammal("CAT");

Почему ты не можешь сказать

m = Animal.CAT;

" CAT "должен будет соответствовать имени перечислений в любом случае, и в вашем примере" CAT " не является производным от параметра, но жестко закодирован.

обновление (возможно, больше к вопросу)

это немного сложно, но...

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

тогда вы можете позвонить

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

ваша реализация find() может выглядеть по-другому (т. е. не исключение).