java - 使用通过反射获得的枚举常量

标签 java enums generics

我有一个通过反射获取枚举值的程序(允许任何枚举),我想通过将它包装在一个以枚举作为其类型参数的泛型类中来对它做一些事情。但是,我不确定如何正确调用构造函数。让它工作的唯一方法是使用原始类型。

(澄清:我的真实程序很复杂,并在运行时从用户提供的文件中查找枚举类名。我的真实程序的包装类具有额外的状态和方法,无法通过枚举,所以我不只是为了学术目的而这样做。我写了下面的示例程序来说明这个问题。它可能看起来是做作的,但它应该是为了说明目的。)

谁能帮我修一下线路

EnumWrapper<?> ewrapped = new EnumWrapper(e);

所以它有一个不那么邪恶的警告?

程序按预期工作(打印出 3 个未找到的枚举常量捕获的异常的堆栈跟踪,否则打印包装的枚举列表),但我养成了从不使用原始类型的习惯,并且不知道如何解决这个问题。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class GenericEnum2 {

    enum Bird { OWL, EAGLE, HAWK };
    enum Mammal { LION, TIGER, BEAR };

    static class EnumWrapper<E extends Enum<E>>
    {
        final private E value;

        public EnumWrapper(E value) { this.value = value; }
        public E getEnum() { return this.value; }
        @Override public String toString() { return "wrapped "+value.toString(); }

        static public <E extends Enum<E>> EnumWrapper<E> wrap(E e) {
            return new EnumWrapper<E>(e);
        }
    }

    public static void main(String[] args) {
        List<EnumWrapper<?>> list = new ArrayList<EnumWrapper<?>>();
        list.add(EnumWrapper.wrap(Bird.OWL));
        list.add(EnumWrapper.wrap(Bird.EAGLE));
        list.add(EnumWrapper.wrap(Bird.HAWK));
        list.add(EnumWrapper.wrap(Mammal.LION));
        list.add(EnumWrapper.wrap(Mammal.TIGER));
        list.add(EnumWrapper.wrap(Mammal.BEAR));        
        System.out.println(list);

        list.clear();
        for (String s : Arrays.asList(
                "Bird.OWL", 
                "Bird.HAWK",
                "Bird.FULVOUS_WHISTLING_DUCK",
                "Mammal.LION", 
                "Mammal.BEAR",
                "Mammal.WARTHOG",
                "Computer.COMMODORE_64"
                ))
        {
            String className = GenericEnum2.class.getCanonicalName()+"$"+s;
            try
            {
                Enum<?> e = getEnum(className);

                // EnumWrapper<?> ewrapped0 = EnumWrapper.wrap(e);
                /*
                 * Bound mismatch: The generic method wrap(E) of type 
                 * GenericEnum2.EnumWrapper<E> is not applicable for 
                 * the arguments (Enum<capture#2-of ?>). The inferred 
                 * type Enum<capture#2-of ?> is not a valid substitute for 
                 * the bounded parameter <E extends Enum<E>>
                 */

                // EnumWrapper<?> ewrapped0 = new EnumWrapper<?>(e);
                // Cannot instantiate the type GenericEnum2.EnumWrapper<?>

                EnumWrapper<?> ewrapped = new EnumWrapper(e);
                // this works but gives me the warning of "EnumWrapper" being a raw type

                list.add(ewrapped);
            }
            catch (IllegalArgumentException e)
            {
                e.printStackTrace();
            }
            catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        System.out.println(list);
    }

    static public Enum<?> getEnum(String enumFullName) throws IllegalArgumentException, ClassNotFoundException
    {
        String[] x = enumFullName.split("\\.(?=[^\\.]+$)");
        if (x.length == 2)
        {
            String enumClassName = x[0];
            String enumName = x[1];
            @SuppressWarnings("unchecked")
            final Class<Enum> cl = (Class<Enum>)Class.forName(enumClassName);
            if (cl.isEnum())
            {
                @SuppressWarnings("unchecked") 
                final Enum result = Enum.valueOf(cl, enumName);
                return result; 
            }
            else
                throw new IllegalArgumentException("Class is not an enum: "+enumClassName);
        }
        return null;
    }
}

编辑:根据 OrangeDog 的建议更新 getEnum():

static public Enum<?> getEnum(String enumFullName) throws IllegalArgumentException, ClassNotFoundException
    {
        String[] x = enumFullName.split("\\.(?=[^\\.]+$)");
        if (x.length == 2)
        {
            String enumClassName = x[0];
            String enumName = x[1];
            final Class<?> cl = Class.forName(enumClassName);
            if (cl.isEnum())
            {
                for (Object o : cl.getEnumConstants())
                {
                    Enum<?> e = (Enum<?>)o;
                    if (enumName.equals(e.name()))
                        return e;
                }
            }
            else
                throw new IllegalArgumentException("Class is not an enum: "+enumClassName);
        }
        return null;
    }
}

最佳答案

当混合泛型和动态时,通常没有办法使所有内容在语法上都是类型安全的。

您通常可以将其减少为一次使用 @SuppressWarnings("unchecked")@SuppressWarnings("rawtypes")然后您可以使用良好的老式逻辑推理证明它是正确的。您必须做出的决定是将“不安全”操作放在哪里。

关于细节,Class<Enum>是错的。你想使用 Class<? extends Enum> (或者只是 Class<?>,因为您正在执行 isEnum() 检查)。

此外,您可以使用 Class<?>.getEnumConstants() 更安全地重建实例。 .遍历查找正确的名称,或直接使用序号进行索引。

关于java - 使用通过反射获得的枚举常量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4937889/

相关文章:

java - 如何从标准输入读取直到 EOF

java - Android Bluetooth_admin 权限错误

c# - 在两个枚举值之间交替

java - 在java中预定义泛型

java - 使用处理语言直观地表示 XML。如何在数据结束/中断的每个点绘制椭圆?

java - 参数化测试: Collection not read by JUnit?

c# - 检查枚举和调用存储库方法的更好方法是什么

java - Enum 反序列化期间未调用 readResolve()

.net - 使用 KeyValuePair<K, V> 的 AutoMapper 没有映射

javascript - 获取 Typescript 以推断联合类型