Получение перечисления с использованием 'valueOf' throws 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");
для поиска конкретного Animal. Тем не менее, я не знал, что если совпадение не найдено, бросается IllegalArgumentException
. Я думал, что, возможно, нуль был возвращен. Так что это дает мне проблему. Я не хочу ловить этот 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" для каждого перечисления млекопитающих. Я полагаю, лучший выбор - просто создать класс утилиты, который принимает аргумент Mammal и ищет его? Возможно, есть более аккуратное решение.
Ответы
Ответ 1
Как насчет создания 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
, если имя не существует
- Это не обнаружит столкновения имен
- Вы можете использовать неизменяемую карту некоторого описания (например, через Guava)
- Возможно, вы захотите написать метод утилиты для создания неизменяемой карты имени к значению для общего перечисления, а затем просто слить карты:)
Ответ 2
Если вы не хотите касаться каждого Enum (понятного), метод утилиты - это путь. Я понимаю, что я не хочу поймать исключение в вашем коде, поймать его в методе должно быть хорошо, хотя:
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;
}
}
Ответ 3
Вы можете использовать библиотеку guava. Enums класс позволяет это:
Enums.valueOfFunction(MyEnum.class).apply(id);
Ответ 4
Вы можете использовать getEnum Метод vom apache EnumUtils. Он возвращает перечисление или null
, если не найден.
EnumUtils.getEnum(Animal.class, "CAT");
Ответ 5
У вас есть ошибка в коде. Если вам нужна ваша функция для возврата 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 зависит от динамики ваших данных. Константы континуума не могут быть созданы во время выполнения. С другой стороны, вам не нужно проверять многие вещи во время выполнения - компилятор сделает это. И вы наверняка забудете что-то проверить. То, что управляет Джон Скит, может дать вам головную боль.
Ответ 6
Я не задаю вопрос, но мне также нужен 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");
Ответ 7
Если вы можете сказать
m = Animal.findMammal("CAT");
Почему вы не можете сказать
m = Animal.CAT;
"CAT" должно будет соответствовать имени перечислений в любом случае и в вашем примере, "CAT" не является производным от параметра, а жестко запрограммирован.
UPDATE
(возможно, более подробно)
Это немного сложнее, но...
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() может выглядеть по-другому (т.е. не выбрасывать исключение).