在Enum中从类初始化一个新对象

时间:2012-03-22 15:42:47

标签: java enums

我有一个名为Plugins的Enum:

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 个答案:

答案 0 :(得分:11)

由于你在迭代插件,我想所有插件的构造函数都有相同的数字和&amp;参数类型。 (我不明白你的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 Enum?

答案 1 :(得分:3)

为插件创建一个界面

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

在枚举上创建一个创建插件实例的方法。

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 Code

答案 2 :(得分:2)

首先,您需要所有类来实现某种接口:

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

当然,你需要一些基本的异常处理等等 - 这段代码是我记得做的匆忙写的(:

答案 3 :(得分:2)

这种情况下的枚举会让你的生活变得更加艰难。

过去我用这种方式解决了这个问题:

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

答案 4 :(得分:1)

是的,您可以使用反射来完成。事实上,我们在项目的枚举中几乎完全相同。它工作得很好,虽然我对这个解决方案创建的所有直接依赖关系并不是很满意。以某种方式使用DI可能更好春天,然而这并没有让我足够实际尝试解决方案。

如果类有默认构造函数,只需调用c.newInstance()即可。如果没有,问题就更复杂了 - here is another post处理这个问题。

当然,一旦你拥有了这些物体,你需要对它们做些什么。标准方法是让所有涉及的类实现相同的接口,然后您可以将对象转换为该接口并在其上调用接口方法。 (当然,在生产代码中,您需要捕获并处理可能出现的所有可能的运行时异常 - 例如InstantiationExceptionIllegalAccessExceptionClassCastException)。

答案 5 :(得分:0)

您的第二个代码段中存在冲突,这让我感到困惑......变量plugins既用于枚举常量,也用作列表。所以我假设这一点,但您应该发布实际上将来可用的代码片段。

所以,是的,有办法做你想做的事:

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

我还建议您查看Service Loader,这是一个非常酷的工具,可以从类路径动态加载服务。

答案 6 :(得分:0)

要创建课程的实例,您可以按照@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());
}

答案 7 :(得分:0)

有两种方式:

  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
    }