【问题标题】:General method for creating enum instances from String从 String 创建枚举实例的通用方法
【发布时间】:2014-03-16 12:39:47
【问题描述】:

假设我有两个实现 Foo 接口的枚举类

public enum Enum1 implements Foo {
  ENUM1_CONST1,
  ENUM1_CONST2;
}

public enum Enum2 implements Foo {
  ENUM2_CONST1,
  ENUM2_CONST2;
}

如何创建一个方法的类型安全实现,该方法将字符串作为参数,该字符串与枚举常量名称的 any 匹配并返回可以是任何常量的 Foo 实例来自两个枚举定义。

换句话说: Foo foo1 = holyGrailMethod("ENUM1_CONST1"); Foo foo2 = holyGrailMethod("ENUM1_CONST2"); Foo foo3 = holyGrailMethod("ENUM2_CONST1"); Foo foo4 = holyGrailMethod("ENUM2_CONST2");

【问题讨论】:

  • 我认为枚举有一个名为valueOf 的静态合成方法可以做到这一点。不是吗?
  • 是的,我尝试使用该方法,但例如以下: Enum.valueOf(Foo.class, "ENUM1_CONST1");给出编译错误。
  • Java - Convert String to enum 的可能重复项
  • 你能告诉我们你是如何尝试使用Enum.valueOf的吗?还有什么错误?
  • 我首先想到的是:public Foo holyGrailMethod(@NonNull final String constantString) { return Enum.valueOf(Foo.class, constantString)}。错误是:绑定不匹配:Enum 类型的通用方法 valueOf(Class, String) 不适用于参数 (Class, String)。推断的类型 Foo 不是有界参数 > 的有效替代品

标签: java generics enums


【解决方案1】:

只需遍历所有枚举类型(您可以将它们放在某个集合中)并调用

Enum.valueOf(EnumType, nameOfConstant) 

但要小心,因为如果检查枚举类型将没有描述字段,此方法将抛出 java.lang.IllegalArgumentException


其他方法就是遍历枚举类型的集合,获取其值(您可以在此处使用Class.getEnumConstants)并检查枚举常量的名称是否与用户传递的名称相同。

public static Enum<?> getEnumFromMany(Collection<Class<?>> enums, String value) {
    for (Class<?> enumType : (Collection<Class<?>>) enums)
        if (enumType.isEnum()) {
            @SuppressWarnings("unchecked")
            Class<Enum<?>> clazz = (Class<Enum<?>>) enumType;
            for (Enum<?> en : clazz.getEnumConstants()) {
                if (en.name().equals(value))
                    return en;
            }
        }
    return null;
}

我使用 Collection> 而不是 Collection&lt;Class&lt;Enum&lt;?&gt;&gt;&gt;,因为泛型不是协变的,所以 Collection&lt;Class&lt;Enum&lt;?&gt;&gt;&gt; 不允许您将 Class&lt;YourEnum&gt;&gt; 添加到自身。

使用示例

public enum DaysILike {
    FRIDAY, SATURDAY, SUNDAY
}

public enum DaysIDontLike {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY
}

public static void main(String[] args) {
    List<Class<?>> enums = new ArrayList<>();
    enums.add(DaysIDontLike.class);
    enums.add(DaysILike.class);
    String yourString = "THURSDAY";
    Enum<?> en = getEnumFromMany(enums, yourString);
    System.out.println(en + " from " + en.getClass());
}

输出:THURSDAY from class SO22436944$DaysIDontLike

【讨论】:

  • 这似乎可以解决问题,谢谢。但是,我担心抑制未经检查的警告。根据当前的实现,我们可以向enums 集合添加一个枚举类型,例如,该枚举类型可以实现Bar 接口(不会出现编译错误)。那么问题是如何用 Java 泛型来表示扩展 Foo 接口的枚举的类类型?我已经尝试了几件事,但我没有做对......
  • 其实我不知道给你什么建议,因为我不知道你将如何使用这种方法。您是说此代码的问题在于它允许添加未实现 Foo 接口的枚举类型,但如果您是创建应该在此方法中迭代的类集合的人,那么您应该能够添加只有正确的枚举类。如果你愿意,你可以添加额外的测试来检查类文字是否也代表 Foo 类型,方法是将 &amp;&amp; Foo.class.isAssignableFrom(fooType) 添加到 if (fooType.isEnum() 条件,因为泛型不能被完全信任。
  • @jilt3d 您也可以考虑仅使用? extends Foo 类型的类列表,并像现在一样在内部检查类是否也是枚举。
  • 该方法在框架代码中,因此我想强制该方法的客户端仅传递扩展 Foo 接口的枚举类型。当然,我可以将这些检查包含在方法的主体中,并在为 false 时抛出异常,但我想在编译器级别强制执行。
  • 我知道在编译时指定它的唯一方法是使用类似于 T extends Enum&lt;T&gt; &amp;&amp; Foo 的东西,就像这个例子中的 public static &lt;T extends Enum&lt;T&gt; &amp; Foo&gt; Foo getEnumFromMany(Collection&lt;Class&lt;T&gt;&gt; foos, String value) {,但是虽然这似乎是正确的方法,但它不是,因为 T在使用中可以表示满足此条件的单个类型,而不是类型族。不幸的是,在 Java 中我们不能使用多类型通配符 ? extends Enum&lt;?&gt; &amp;&amp; Foo 来解决这个问题。
猜你喜欢
  • 1970-01-01
  • 2018-04-18
  • 1970-01-01
  • 1970-01-01
  • 2015-10-19
  • 1970-01-01
  • 2019-12-21
  • 1970-01-01
  • 2021-03-17
相关资源
最近更新 更多