2009-10-02 2 views
18

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

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

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

Fruit.values()[i].fruitname 

, но я хотел бы быть в состоянии сделать что-то вроде (pseduo-код):

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

Возможно ли это? Должен ли я использовать что-то еще (Массивы? Карты?)?

EDIT: в конце концов я пошел с предложением NawaMan, но спасибо всем за полезный вход.

ответ

22

Я действительно не знаю встроенного решения. Поэтому вам, возможно, придется писать его как статический метод.

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 ​​()» каждый раз создает клонированный массив, поэтому лучше не называть его слишком часто. Вызывайте его только один раз и кэшируйте результаты, или используйте «EnumSet.allOf (Fruit.class)». – dogbane

+1

В JDK 1.7 исправлено. В JDK 1.5 есть комментарий, чтобы исправить это позже. Не знаю, что произойдет в JDK 1.6. – alexsmail

+1

Обратите внимание, что это решение является медленным для многих значений. Лучше сделайте что-то вроде http://stackoverflow.com/a/2546726/260805. – Ztyx

7

Когда я это делаю, я обычно пересаживаю его на класс 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); 
    } 
} 

Но:

  • Для небольших перечислений это, вероятно, более эффективно перемещаться по списку.

Кстати:

  • В этой ситуации я бы пошел с конвенцией и используется имя(), так как это то же самое, как пользовательское имя, за исключением случая
  • Это решение (легко исправлена.) более полезно, когда то, что вам нужно найти, полностью отличается от значения name().
+0

Я принял разрешение зафиксировать пример, имея карту как статические. – KLE

+0

Порядок инициализации в порядке, не беспокойтесь. – KLE

+0

Да, отсутствие статики было опечаткой. – Trejkaz

2

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


Я думаю for loop вы говорите правильно, если у вас есть только несколько значений перечислений. Вероятно, он будет иметь лучшую производительность. Но я понимаю, что ты этого не хочешь.


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

Обновление: Trejkaz уже разместил код, который делает это.


отметить также, что иногда, вместо того чтобы вернуться null в качестве возвращаемого типа, когда экземпляр не соответствует, некоторые перечисления имеют специальный экземпляр для этого (назовем его EMPTY или NOT_FOUND, например). Преимущество состоит в том, что все вызывающие коды не должны иметь дело с нулями, а риск - NullPointerException. При необходимости может быть логический метод, который говорит isFound() (возвращает true, за исключением этого экземпляра).И коды, которые действительно должны отличаться от того, что ценности от других по-прежнему могут, в то время как те, которые не заботятся, просто передают экземпляр вокруг без знания этого специального случая.

+2

Touche. Хороший звонок с «не исключительным условием». Я думал, что исключение следует рассматривать как таковое. Если это не исключение, это не должно быть исключением. +1, @KLE. – Rap

5

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

Если вы проверяете бизнес-логику, а не пользовательский интерфейс, на этом уровне не будет обратной связи. (Если вы не проверяете пользовательский интерфейс, у нас есть другие проблемы). Поэтому правильный способ справиться с этим - это исключение.

Конечно, это не значит, что вы должны обладать исключительным пузырем до уровня пользовательского интерфейса, таким образом, замыкая всю оставшуюся часть вашей логики. Обычно я делаю это перечисление перечисления в своем собственном маленьком пробке и обрабатываю исключение путем переназначения или любого другого элегантного решения, которое вы разработали.

Короче ... вы были на деньгах с вашей первой мыслью. Смирись с этим. Просто измените обработку исключений немного иначе.

2

Возможно, вам не следует использовать Enum? Если вам регулярно приходится иметь дело со значениями, которые не определены в вашем Enum, возможно, вы должны использовать что-то вроде HashMap < String, Fruit > Затем вы можете использовать containsKey(), чтобы узнать, существует ли какой-либо конкретный ключ.

2

Просто упомянуть еще одну возможность, которая позволит вашему вызывающему коду не беспокоиться об исключениях или условных проверках, - это всегда возвращать Фрут. Если строка не найдена, верните, например, Fruit.UNKNOWN.

Пример:

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

Это, как вы можете сделать это с помощью EnumSet.allOf для заполнения карты:

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

Существует Апачи Обще языки EnumUtils.isValidEnum(). К сожалению, под капотом, это использование попробовать/поймать логику и возвращает логическое значение, но, по крайней мере, ваш код выглядит чистым:

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

Вам нужно будет использовать последнюю библиотеку Обще-lang3, как Викисклада языки 2.x не имеет этой функции.

+0

также проверяет нулевые значения, это достаточно хорошо. Спасибо, что ваш комментарий был полезным –

+1

Я не знал об этой функции. Это действительно самый простой и простой способ (если у вас есть зависимость) – Seega

+0

он не проверяет значение. например если есть APPLE («яблоко»), а вызов для яблока, он вернет false. Если вызов для APPLE, он вернет true. – Dejell

3

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

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

В java8 вы можете сделать это, как этот

public static boolean isValidFruit(final String fruit) { 
    return Arrays.stream(Fruit.values()) 
     .map(Fruit::name) 
     .collect(Collectors.toSet()) 
     .contains(fruit); 
} 
Смежные вопросы