簡體   English   中英

使用通過反射獲得的枚舉常量

[英]using enum constants obtained through reflection

我有一個程序可以通過反射獲取一個Enum值(允許任何Enum),我想通過將其包裝在一個以Enum作為其類型參數的泛型類中來做一些事情。 我不確定如何正確調用構造函數。 我可以使其正常工作的唯一方法是使用原始類型。

澄清:我的真實程序很復雜,並且在運行時從用戶提供的文件中查找枚舉類名。我的真實程序的包裝器類具有無法用枚舉完成的其他狀態和方法,所以我不僅僅是這樣做為了學術上的考慮,我在下面編寫了示例程序來說明這個問題。它看起來像是人為的,但應該出於說明目的。)

誰能幫我解決這個問題

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<? extends Enum> (或者只是在您進行isEnum()檢查時只是Class<?> )。

另外,您可以使用Class<?>.getEnumConstants()更安全地重建實例。 通過尋找正確的名稱進行迭代,或者直接使用序數索引。

為了包裝枚舉對象,我們需要枚舉的實際類型,就像在您的包裝方法中一樣(將其作為類型參數就足夠了)。 但是為此,編譯器需要推斷類型,在這種情況下,這似乎是不可能的。

問題在於,雖然Enum<E>每個實例實際上都是E類型,但Enum<E>實際上對於編譯器而言與E不兼容。 這是因為<E extends Enum><E extends Enum<E>>不是兼容的類型變量聲明,因此我們無法推斷出

<E extends Enum<E>> E getInstance(Class<E> c, String name)

當使用Class<?>類型的參數調用時。

就是說,我找到了一種方法,其中僅發生一個未經檢查的警告,這顯然是錯誤的警告:

public class GenericEnum2 {
    ...
    public static void main(String[] args) {

        ...
        for(...) {
            ...
            try
            {
                Enum<?> e = getEnum(className);
                EnumWrapper<?> wrapper = makeWrapper(e);
                list.add(wrapper);
            }
            ...
        }
        ...
    }

    /**
     * casts an instance of a Enum to its right type.
     */
    static <E extends Enum<E>> E cast(Enum<E> e) {
        // @SuppressWarning("unchecked")
        E result = (E)e;
        return result;
    }

    /**
     * makes a wrapper for an enum instance.
     * @see EnumWrapper#wrap
     */
    static <E extends Enum<E>> EnumWrapper<E> makeWrapper(Enum<E> e) {
        return EnumWrapper.wrap(cast(e));
    }

    ...

}

直接將EnumWrapper.wrap(cast(e))放在main方法中不起作用EnumWrapper.wrap(cast(e))無法推斷出wrap將接受與返回的cast相同的類型。

通過將getEnum方法拆分為幾種方法並進行類似的轉換,可以避免某些SuppressWarnings,但我並未真正嘗試過。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM