Преобразовать строку в значение перечисления с помощью отражения, где тип перечисления может быть любым из нескольких - PullRequest
1 голос
/ 02 мая 2020

Предположим, у меня есть класс, который принимает другой объект в качестве идентификатора:

public SomeClass
{
    private final ID id;

    ...
}

ID определяется следующим образом. Обратите внимание, что причина того, что перечисление разделено (на логические группировки), состоит в том, что одно перечисление в противном случае содержало бы более 1000 значений. В этом случае необходимо, чтобы все перечисления относились к одному и тому же типу.

public interface ID
{
    public enum Name1 implements ID { ... constants ... }
    public enum Name2 implements ID { ... constants ... }
    public enum Name3 implements ID { ... constants ... }

    ...
}

И объект SomeClass создается следующим образом:

SomeClass object = new SomeClass(ID.Name2.SOME_VALUE, ... more parameters};

Однако параметры, необходимые для создания объекта SomeClass, хранятся в файле json, например:

{
    "id": "SOME_VALUE",

    ...
}

Я хочу сопоставить строку "SOME_VALUE" с ID.Name2.SOME_VALUE. Теперь я мог бы сделать это, имея гигантскую карту:

Map<String, ID> conversionMap = HashMap<>();
conversionMap.put("SOME_VALUE", ID.Name2.SOME_VALUE);
conversionMap.put("SOME_OTHER_VALUE", ID.Name3.SOME_OTHER_VALUE);
... etc

, но я хочу сделать это автоматически, используя отражение от метода stati c внутри интерфейса ID (некоторый очень грубый псевдокод):

public interface ID
{
    public static ID getIdFromString(String key)
    {
        List<Enum> declaredEnums = ID.class.getDeclaredEnums();
        for (Enum declaredEnum : declaredEnums)
        {
            for (EnumValue value : declaredEnum)
            {
                if (value.equals(key)
                    return value;
            }
        }
    }

    public enum Name1 implements ID { ... constants ... }
    public enum Name2 implements ID { ... constants ... }
    public enum Name3 implements ID { ... constants ... }

    ...
}

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

Обратите внимание, что я мог бы также реализовать это, используя более 1000 целочисленных констант и обеспечивая строка> целочисленное отображение для этого, но использование перечислений чувствует себя чище. Теперь, когда я наткнулся на эту загадку, я уже не так уверен в чистоте. Мне начинает казаться, что я пытаюсь вставить круглый колышек в квадратное отверстие.

ОБНОВЛЕНИЕ: я решил использовать принятый ответ в качестве решения и чуть-чуть изменил его для работы с моим кодом:

public static ID getIdFromString(String key)
{
    Optional<?> id = Arrays.stream(ID.class.getDeclaredClasses())
        .filter(Class::isEnum)
        .flatMap(aClass -> Arrays.stream(aClass.getEnumConstants()))
        .filter(enumValue -> enumValue.toString().equals(key))
        .findFirst();

        return (ID)id.get();
}

Помните, что этот код вообще не выполняет никакой проверки, поэтому вам, вероятно, следует добавить к нему некоторые проверки для обработки недействительных ключей и, возможно, даже для обработки перечислений, объявленных в ID, но не реализуйте интерфейс ID.

1 Ответ

0 голосов
/ 02 мая 2020

Вы можете получить все объявленные классы через отражение (включая перечисления) и сделать что-то вроде этого:

 Arrays.stream(getClass().getDeclaredClasses())
      .filter(Class::isEnum)
      .flatMap(aClass -> Arrays.stream(aClass.getEnumConstants()))
      .filter(enumValue -> enumValue.toString().equals(YOUR_VALUE_FROM_JSON))
      .findFirst();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...