Проверьте допустимые значения перечисления перед использованием перечисления - PullRequest
22 голосов
/ 02 октября 2009

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

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

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

Fruit.values()[i].fruitname

но я бы хотел иметь возможность сделать что-то вроде (псевдо-код):

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

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

РЕДАКТИРОВАТЬ: в конце концов я согласился с предложением NawaMan, но спасибо всем за полезный вклад.

Ответы [ 11 ]

0 голосов
/ 15 июля 2018

В Oracle JDK (пробовал с JDK 10.0.1) класс Class имеет поле enumConstantDirectory. Это поле имеет тип Map<String, T> для Class<T>. Он хранит константы перечисления T по их именам. После инициализации класса enum enumConstantDirectory все еще пуст. При первом вызове Enum.valueOf(Class<T> enumType, String name) все константы данного перечисления T сохраняются в enumConstantDirectory.

Поскольку каждый класс enum уже имеет свое собственное отображение, мы можем попытаться использовать его вместо создания дополнительного локального отображения для / some / every enum / s.

Сначала я реализовал служебный класс:

  public class Enums {

    private static final Field DIRECTORY_FIELD;

    static {
      try {
        DIRECTORY_FIELD = Class.class.getDeclaredField("enumConstantDirectory");
      }
      catch (Exception e) {
        throw new RuntimeException(e);
      }
    }

    public static <T extends Enum<T>> T valueOfOrDefault(Class<T> enumType, String name, T defaultValue) throws Exception {
      return getEnumConstantDirectory(enumType).getOrDefault(name, defaultValue);
    }

    public static <T extends Enum<T>> boolean hasValueFor(Class<T> enumType, String name) throws Exception {
      Map<String, T> enumConstantDirectory = getEnumConstantDirectory(enumType);
      return enumConstantDirectory.containsKey(name);
    }

    private static <T extends Enum<T>> Map<String, T> getEnumConstantDirectory(Class<T> enumType) throws Exception {
      try {
        DIRECTORY_FIELD.setAccessible(true);
        Map<String, T> enumConstantDirectory = (Map<String, T>) DIRECTORY_FIELD.get(enumType);
        return enumConstantDirectory;
      }
      finally {
        DIRECTORY_FIELD.setAccessible(false);
      }
    }

  }

Может использоваться следующим образом:

  public enum Note {

    DO, RE, MI, FA, SOL, LA, SI;

    static {
      Enum.valueOf(Note.class, Note.DO.name());
    }

    public static Note valueOfOrDefault(String name, Note defaultValue) throws Exception {
      return Enums.valueOfOrDefault(Note.class, name, defaultValue);
    }

    public static <T extends Enum<T>> boolean hasValueFor(String name) throws Exception {
      return Enums.hasValueFor(Note.class, name);
    }

  }

Подведем итог:
Как правило, можно проверить, представляет ли имя константу перечисления без дополнительных отображений или итерации по константам перечисления. Но, как всегда, с отражениями есть известные недостатки. Кроме того, необходимо убедиться, что константы перечисления хранятся в его классе.

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