【问题标题】:How do I get the value of an Enum, if I don't know the class at compile time?如果我在编译时不知道类,如何获得 Enum 的值?
【发布时间】:2011-03-10 15:46:21
【问题描述】:

我正在尝试执行以下操作:

Class<?> cls = unknownClass;
if(cls.isEnum()){
    @SuppressWarnings("unchecked")
    Class<? extends Enum<?>> enumClass = (Class<? extends Enum<?>>) cls;
    Object val = Enum.valueOf(enumClass, "NAME1");
}

但我收到以下错误:

Bound mismatch: The generic method valueOf(Class<T>, String) of type Enum<E> is 
not applicable for the arguments (Class<capture#5-of ? extends Enum<?>>, String). 
The inferred type capture#5-of ? extends Enum<?> is not a valid substitute for 
the bounded parameter <T extends Enum<T>>   

谁能告诉我我做错了什么?

【问题讨论】:

    标签: java generics reflection enums


    【解决方案1】:

    鉴于演员不会真的检查事情,我会选择完全原始的版本:

    if (cls.isEnum()){
        @SuppressWarnings("unchecked")
        Object val = Enum.valueOf(cls, "NAME1");
    }
    

    这似乎行得通。完整示例:

    public class Test
    {
        enum Foo
        {
            BAR, BAZ
        }
    
    
        public static void main(String[] args)
        {
            @SuppressWarnings("rawtypes")
            Class cls = Foo.class;
    
            if (cls.isEnum())
            {        
                @SuppressWarnings("unchecked")
                Object value = Enum.valueOf(cls, "BAR");
                System.out.println(value);
            }
        }
    }
    

    【讨论】:

    • @dacwe:它在 my 编译器上工作 - 但你没有说它在什么 方式 上不起作用。我将展示一个简短但完整的示例...
    • 啊,抱歉,它无法编译。 Class&lt;?&gt; cls = SomeEnum.class; Object val = Enum.valueOf(cls, "NAME1"); 我得到了一个类似的 Bound mismatch 作为提问者。
    • @dacwe:你没有向我们展示cls 的声明。只需将其更改为原始类型 Class 而不是 Class&lt;?&gt;。有关完整示例,请参阅我的编辑。
    • (如果您不能更改cls 的声明,请使用@SuppressWarnings("rawtypes") Class clazz = cls;,然后改用clazz
    【解决方案2】:

    问题是您有两个?,它们可能不同,但必须相同。

    你要么必须使用非泛型来声明泛型类型,比如

    public static <T extends Enum<T>> T val(Class<T> cls) {
        if(cls.isEnum()) { // is redundant when the compiler checks this.
            @SuppressWarnings("unchecked")
            Class<T> enumClass = (Class<T>) cls;
            T val = Enum.valueOf(enumClass, "NAME1");
            return val;
        }
        return null;
    }
    

    然而,调用这个方法也有点像噩梦。 ;)

    【讨论】:

    • T 被定义为扩展 Enum&lt;T&gt; 的类这是由于枚举的 public class Enum&lt;E extends Enum&lt;E&gt;&gt; 的繁琐/递归声明
    • @Peter:是的,但由于调用者无法与 T 交互,因此它将被完全删除。
    • 如你所写,我如何如何调用该方法? :)
    • 您可以简单地调用val(MyEnum.class),但是编译器可能会在尝试确定返回类型时感到困惑,因为它不受参数之一的约束,因此您最终会以更明智的方式编写MyClass.&lt;MyEnum,MyEnum&gt;val(MyEnum.class)例如,您不需要检查 isEnum() 和 V=T 并且它更简单。
    • 你必须把它转换成(Class)
    【解决方案3】:

    Class.getEnumConstants 将提供所有枚举,然后您可以找到您感兴趣的枚举。

    修改 Jon 的代码:

    public class Test {
        private enum Foo {
            BAR, BAZ
        } 
    
        public static void main(String[] args) {
            Class<?> cls = Foo.class;
    
            if (cls.isEnum()) { 
                for (Object constant : cls.getEnumConstants()) { 
                    Enum<?> enumConstant = (Enum<?>)constant;
                    if (enumConstant.name().equals("BAR")) {
                        System.out.println(constant);
                        break;
                    }
                }
            }
        }
    }
    

    在 Java SE 8 中,您可能会使用 lambda 和抽象控制流来做一些聪明的事情。

    注意:

    • 反思几乎总是一个非常糟糕的主意。
    • 不需要未经检查的警告或抑制那些有效的警告。
    • 不需要原始类型(无论是拼写和语义)警告或抑制这些有效警告。
    • 不需要毫无根据的咆哮。
    • 无需对此答案投反对票。

    【讨论】:

    • 我喜欢如果我在编译时不知道枚举类型,这个解决方案不会抛出编译器警告。我可以只拥有一个 foo 类型的实例并调用 fooInstance.getClass() 并将其用于 cls。
    【解决方案4】:

    使用原始类型很好。虽然 Java 不愿意添加原始类型,但它已被证明是必要且不可或缺的,除了向后兼容性问题。

    如果没有原始类型,要以理论上完美的方式解决您的问题,Java 必须要复杂得多。它可能需要一个类型变量。到那时,代码不再是为了取悦人类,而是为了取悦机器。 (考虑到所有无法理解的泛型代码,我们可能已经到了这一点)

    【讨论】:

      【解决方案5】:
          class EnumValues<P extends Enum<P>>
          {
              public static <P extends Enum<P>> P [] getValues( Class<P> keyType)
              {
                  return keyType.getEnumConstants();
              }
          }
      

      用法:

          Class cls = Thread.State.class;
          for( Enum en : EnumValues.getValues( cls)) 
              System.out.println( en.name()); 
      

      【讨论】:

      • 使用原始类型调用泛型方法有点不礼貌。实际上那里有一个未经检查的演员表。最近的编译器会给你警告(注意它们!)。 (不是我的反对票,顺便说一句。)
      猜你喜欢
      • 2016-06-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-03
      • 2011-12-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多