Инициализировать новый объект из класса в Enum - PullRequest
6 голосов
/ 22 марта 2012

У меня есть Enum под названием Plugins:

public enum Plugins {

    ROTATING_LINE (plugin.rotatingline.RotatingLine.class),
    SNOW_SYSTEM (plugin.snow.SnowSystem.class);

    private Class<?> c;

    private Plugins (Class<?> c) {
        this.c = c;
    }

    public Class<?> getClassObject() {
        return c;
    }

}

Что я хотел бы сделать, это перебрать все перечисления в Plugins и создать новые объекты из тех, которые используют переменную c, например:

for (Plugins plugins : Plugins.values()) {
    Class<?> c = plugins.getClassObject();
    pluginList.add(new c(400, 400));
}

Есть ли способ сделать это подобным способом? Причина, по которой я хочу сделать это, состоит в том, чтобы создать список классов, которые должны быть добавлены в плагины List при запуске моего приложения.

Ответы [ 8 ]

11 голосов
/ 22 марта 2012

Поскольку вы перебираете плагины, я думаю, что конструкторы всех плагинов имеют одинаковое количество и тип параметров.(Хотя я не понимаю ваших plugins.add(...).)

В любом случае, если вы хотите использовать Enums, я бы сделал следующее, используя реализации метода, специфичного для констант, вместо токенов типа / отражение :

public enum PluginTypes {
   ROTATING_LINE { Plugin create(int x, int y){
         return new plugin.rotatingline.RotatingLine(x,y);} },
   SNOW_SYSTEM { Plugin create(int x, int y){
         return new plugin.snow.SnowSystem(x,y);} };

   abstract Plugin create(int x, int y);
}

public interface Plugin {
  //...
}

Тогда ваш цикл будет выглядеть примерно так:

List<Plugin> plugins = new ArrayList<Plugin>();

for (PluginTypes pt : PluginTypes.values()) {
    plugins.add(pt.create(400, 400));
}

С другой стороны, если вы все равно знаете классы реализации Plugin, почему бы не использовать их напрямую вместо этогоиз через перечисление PluginTypes?

3 голосов
/ 22 марта 2012

Создать интерфейс для плагинов

public interface Plugin{
     setShapeType();
     setX();
     setY();
     ...
  }

Создайте метод enum, который создает экземпляр Plugin.

public enum Plugins {

        ROTATING_LINE (plugin.rotatingline.RotatingLine.class),
        SNOW_SYSTEM (plugin.snow.SnowSystem.class);

        private Class<? extends Plugin> c;

        private Plugins (Class<? extends Plugin> c) {
            this.c = c;
        }

        // This acts as a constructor that takes args, I am assuming the args you need 
        public Class<? extends Plugin> createInstance(String shapeType,int x, int y) {
            Plugin plugin = c.newInstance();
            plugin.setShapeType(shapeType);
            plugin.setX(x);
            plugin.setY(y);
            return plugin;
        }

    }

Создание в цикле

List<Plugin> myPlugins = new ArrayList<Plugin>();

for (Plugins plugins : Plugins.values()) {
    myPlugins.add(plugin.createInstance(Shaped.parent, 400, 400));
}

Обратите внимание, что это код Psuedo

2 голосов
/ 22 марта 2012

Перечисления в этой ситуации сделают вашу жизнь более сложной.

В прошлом я решал эту проблему следующим образом:

class PluginFactory<T extends Plugin> {

        private Class<T> clazz;

        PluginFactory(Class<T> clazz) {
            this.clazz = clazz;
        }

        T newInstance() {
            return clazz.newInstance();
        }

    final static PluginFactory<SnowSystem> SNOW_SYSTEM_FACTORY = 
            new PluginFactory(SnowSystem.class);
    ...

    // should really use a list and a wilcard generic bounded by Plugin, but it's
    // too verbose for me for the purposes of this answer
    final static PluginFactory[] FACTORIES = {SNOW_SYSTEM_FACTORY, ...};

    public static void main(String[] args) {
        Plugin[] arr = new Plugin[FACTORIES.length];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = FACTORIES[i].newInstance();
        }

        // more usefully though
        SnowSystem ss = SNOW_SYSTEM_FACTORY.newInstance();
        ss.setValue(123);
    }

}

Другой вариант - дать аргументы newInstance var argsпараметр объекта.Затем используйте отражения, чтобы найти подходящий конструктор, который принимает эти типы в качестве параметров.Это ужасно грязно и совершенно небезопасно, если пользователь API дает неверный набор аргументов (исключение выдается).

public T newInstance(Object... args) {    
    for (Constructor c : clazz.getConstructors()) {
        if (isMatchingConstructor(c, args)) {
            return clazz.cast(c.newInstance(args));
        }
    }
    throw new NoSuchMethodException("No matching constructor found for args: " 
            + Arrays.toString(args));
}

private boolean isMatchingConstructor(Constructor c, Object... args) {
    Class<?>[] parameters = c.getParameterTypes();

    if (parameters.length != args.length) {
        return false;
    }

    for (int i = 0; i < args.length; i++) {
        if (!parameters[i].isAssignableFrom(args[i].getClass())) {
            return false;
        }
    }
    return true;
}
2 голосов
/ 22 марта 2012

Во-первых, вам нужны все классы для реализации какого-то интерфейса:

public interface Plugin {
    public doSomething();
}

Тогда вы можете сделать

Class clazz = getClassObject();
Plugin plugin = clazz.newInstance();
... add to a list where you can use later ...

Что касается загрузки ваших плагинов, вы можете объявить их все в enum ... или вы можете объявить их в конфигурации .xml или .properties (например), которая даст вам больше гибкости:

public void loadPlugins(){
    List<Plugin> plugins = new ArrayList<Plugin>();    
    ... load plugin names from config file ...
    for(String pluginClass : pluginsConfigList)
       plugins.add(Class.forName(pluginClass).newInstance());
}

Конечно, вам понадобится некоторая базовая обработка исключений и т. Д. - этот код был написан на скорую руку из того, что я помню, делал (:

1 голос
/ 22 марта 2012

Да, вы можете сделать это с помощью отражения.Фактически, мы делаем почти то же самое в перечислении в нашем проекте.Это работает хорошо, хотя я не на 100% доволен всеми прямыми зависимостями, которые создает это решение.Может быть, лучше как-то использовать DI через, например, Spring, однако это не доставило мне особых хлопот, чтобы на самом деле поэкспериментировать с решением.

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

Конечно, когда у вас есть объекты, вам нужно что-то с ними сделать.Стандартный способ состоит в том, чтобы все участвующие классы реализовали один и тот же интерфейс, тогда вы можете преобразовать объекты в этот интерфейс и вызвать методы интерфейса для них.(Конечно, в рабочем коде вам нужно перехватить и обработать все возможные исключения времени выполнения, такие как InstantiationException, IllegalAccessException и ClassCastException).

0 голосов
/ 06 июня 2017

Есть 2 способа:

  1. использовать Enum.valueOf() статическую функцию, затем привести ее к вашему типу перечисления.

    Enum v = Enum.valueOf(TheEnumClass, valueInString);
    
  2. Используйте функцию class.getEnumConstants(), чтобы получить список констант перечисления, зациклите этот список и получите.

    Plugins[] plugins = Plugins.class.getEnumConstants();
    for (Plugins plugin: plugins) {
        // use plugin.name() to get name and compare
    }
    
0 голосов
/ 22 марта 2012

Для создания экземпляра класса вы можете следовать ответу от @Peter и для хранения ссылки на этот объект, который я предлагаю EnumMap .

EnumMap<Plugins, Object> map = new EnumMap<Plugins, Object>(Plugins.class);
for (Plugins plugins : Plugins.values()) {
    Class<?> c = plugins.getClassObject();
    map.put(plugins, c.newInstance());
}
0 голосов
/ 22 марта 2012

Во втором фрагменте кода есть конфликт, который меня немного смущает ... переменная plugins используется как для констант enum, так и в виде списка. Итак, я предполагаю это, но вы должны опубликовать фрагменты кода, которые действительно будут работать в будущем.

Итак, да, есть способ сделать то, что вы хотите:

for (Plugins plugins : Plugins.values()) {
    Class<?> c = plugins.getClassObject();
    pluginsList.add(c.getConstructor(Integer.TYPE, Integer.TYPE).newInstance(400, 400));
}

Также я рекомендую вам посмотреть Service Loader , который является действительно классным инструментом для динамической загрузки сервисов из classpath.

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