【问题标题】:Convert string to enum value using reflection, where the enum type can be any of several使用反射将字符串转换为枚举值,其中枚举类型可以是多个
【发布时间】:2023-04-04 23:09:01
【问题描述】:

假设我有一个将另一个对象作为 id 的类:

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

但我想使用来自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接口。

【问题讨论】:

  • 为什么要创建多个枚举,而不是一个,尤其是考虑到它们嵌套在界面内?
  • 因为当我在 Java 中寻找大型枚举的最佳实践时,我发现了这个问题和答案:stackoverflow.com/questions/8671088/…
  • 枚举是编译时常量,这意味着当代码必须按名称引用它们时,它们最有用。如果您有这么多枚举,您正在考虑将它们拆分,那么您真的是在代码中按名称指代所有枚举吗?或者你真的只是通过字符串值来引用它们,例如数据库?如果是后者,那么它们首先不应该是枚举。
  • 是的,每个枚举中的每个值都将在代码中的某处至少被引用一次(有些将被多次引用)。正如我所说,我也可以使用一个包含所有必需的字符串/整数常量的类和一个映射函数,将标识符从 json 文件映射到内部字符串/整数常量,但枚举并不是为了避免使用这些而设计的“正常”常数?请注意,我没有直接使用外部字符串标识符作为这些对象的键,因为我只想在代码中使用有效的标识符。
  • 您的问题似乎是一种字符串实习问题。因此,通过定义String-constants 和您的方法String getIdFromString(String str) 将只委托给str.intern(),您可能会更有效率。巨型地图是 jvm 字符串表,查找可能比自定义巨型地图(但 O(1) 但可能更高的常数)或反射(O(n),其中 n 是常数的数量)更有效。

标签: java reflection enums


【解决方案1】:

您可以通过反射获取所有声明的类(包括枚举)并执行以下操作:

 Arrays.stream(getClass().getDeclaredClasses())
      .filter(Class::isEnum)
      .flatMap(aClass -> Arrays.stream(aClass.getEnumConstants()))
      .filter(enumValue -> enumValue.toString().equals(YOUR_VALUE_FROM_JSON))
      .findFirst();

【讨论】:

  • 我(作为程序员)确实知道在ID 接口中定义了哪些枚举,并且可以编写循环搜索getIdFromString 中的每个枚举,但我不想继续添加每次我添加或删除枚举时(或删除)getIdFromString 方法的循环。使用反射可以使这个工作与任意数量的声明的枚举一起工作。
  • 知道了。我已经更新了我的答案。告诉我是否有帮助
  • 感谢您的回答,但这似乎没有返回任何内容,即它返回的 Optional 是空的(即使我传入的字符串与枚举值之一的名称完全匹配)。
  • 对不起,我写错了代码。我已经更新了答案。请尝试一下
  • 这行得通,我现在可以从Optional 检索对象并将其转换为ID。再次感谢您的帮助!
猜你喜欢
  • 2018-02-14
  • 1970-01-01
  • 2023-02-21
  • 1970-01-01
  • 1970-01-01
  • 2019-02-27
  • 1970-01-01
  • 2010-10-03
相关资源
最近更新 更多