используя константы перечисления, полученные посредством отражения - PullRequest
2 голосов
/ 08 февраля 2011

У меня есть программа, которая получает значение Enum посредством отражения (любой Enum разрешен), и я хочу сделать что-то с ним, обернув его в общий класс, который принимает Enum в качестве параметра типа. Однако я не уверен, как правильно вызывать конструктор. Единственный способ заставить его работать - использовать необработанный тип.

( уточнение: Моя настоящая программа сложна и ищет имя класса enum из предоставленного пользователем файла во время выполнения. Класс-оболочка моей реальной программы имеет дополнительное состояние и методы, которые невозможно выполнить с помощью enum , поэтому я не просто делаю это ради академичности. Я написал пример программы ниже, чтобы проиллюстрировать проблему. Может показаться надуманным, но это должно быть сделано в иллюстративных целях.)

Может кто-нибудь помочь мне исправить линию

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

ниже, так что у него меньше злого предупреждения?

Программа работает должным образом (выводит следы стека 3 отловленных исключений для констант enum, не найденных, в противном случае выводит списки завернутых перечислений), но я делаю привычкой никогда не использовать необработанные типы и не знаю, как это сделать исправить это дело.

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;
    }
}

edit: обновлено getEnum () в соответствии с предложениями OrangeDog:

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;
    }
}

Ответы [ 2 ]

4 голосов
/ 09 февраля 2011

При смешивании обобщений и динамики обычно нет способа сделать все синтаксически безопасным для типов.

Обычно вы можете уменьшить его до однократного использования @SuppressWarnings("unchecked") или @SuppressWarnings("rawtypes"), которое затем вы можете подтвердить, используя правильные логические рассуждения старого образца Решение, которое вы должны принять, - это то, куда вы помещаете «небезопасную» операцию.

По конкретике, Class<Enum> неверно. Вы хотите использовать Class<? extends Enum> (или просто Class<?>, поскольку вы все равно делаете проверку isEnum()).

Кроме того, вы можете более безопасно восстанавливать экземпляр, используя Class<?>.getEnumConstants(). Либо итерируйте в поисках правильного имени, либо индексируйте напрямую, используя порядковый номер.

1 голос
/ 09 февраля 2011

Для переноса объекта enum нам нужен фактический тип enum, как в вашем методе wrap (достаточно, чтобы это было параметром типа). Но для этого компилятор должен определить тип, и, похоже, в этом случае это невозможно.

Проблема в том, что хотя каждый экземпляр 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 не сработало - там javac не смог сделать вывод, что wrap будет принимать тот же тип, что и возвращаемое приведение.

Возможно, можно избежать некоторых SuppressWarnings в вашем методе getEnum, разделив его на несколько методов и сделав аналогичные приведения, но я действительно не пробовал.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...