在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插件。

由于您正在迭代插件,我想所有插件的构造函数都具有相同数量和类型的参数。 (虽然我不了解你的plugins.add(...) 。)

无论如何,如果你想使用Enums,我会使用常量特定的方法实现而不是类型标记/reflection来执行以下操作

 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 plugins = new ArrayList(); for (PluginTypes pt : PluginTypes.values()) { plugins.add(pt.create(400, 400)); } 

另一方面,如果您知道插件的实现类,为什么不直接使用它们而不是通过PluginTypes Enum?

为插件创建一个接口

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

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

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

实例化循环

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

请注意这是Psuedo Code

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

 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 plugins = new ArrayList(); ... load plugin names from config file ... for(String pluginClass : pluginsConfigList) plugins.add(Class.forName(pluginClass).newInstance()); } 

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

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

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

 class PluginFactory { private Class clazz; PluginFactory(Class clazz) { this.clazz = clazz; } T newInstance() { return clazz.newInstance(); } final static PluginFactory 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对象参数。 然后使用reflection来查找将这些类型作为参数的相应构造函数。 如果API的用户提供了一组错误的参数(抛出exception),这是非常混乱并且完全不安全。

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

是的,你可以用reflection来做。 事实上,我们在项目的枚举中几乎完全相同。 虽然我对这个解决方案创建的所有直接依赖关系并不十分满意,但它运行良好。 通过例如Spring以某种方式使用DI可能会更好,但是这并没有足以让我真正尝试解决方案。

如果类有默认构造函数,只需调用c.newInstance() 。 如果没有,问题就更复杂了 – 这是另一个处理这个问题的post 。

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

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

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

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

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

要创建类的实例,您可以按照@Peter的回答来保存对该对象的引用,我建议使用EnumMap 。

 EnumMap map = new EnumMap(Plugins.class); for (Plugins plugins : Plugins.values()) { Class c = plugins.getClassObject(); map.put(plugins, c.newInstance()); } 

有两种方式:

  1. 使用Enum.valueOf()静态函数,然后将其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 }