Java 从枚举中的类初始化新对象

Java 从枚举中的类初始化新对象,java,enums,Java,Enums,我有一个名为插件的枚举: 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; } pu

我有一个名为插件的枚举:

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

}

有没有办法用类似的方法来实现这一点?我想这样做的原因是创建一个类列表,当我启动应用程序时,这些类应该添加到列表插件中。

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

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());
}
public void loadPlugins(){
List plugins=new ArrayList();
…从配置文件加载插件名称。。。
用于(字符串pluginClass:pluginsConfigList)
add(Class.forName(pluginClass.newInstance());
}

当然,您将需要一些基本的异常处理,等等-这段代码是根据我记得的操作匆忙编写的(:

是的,您可以使用反射来完成。事实上,我们在我们的项目中的枚举中做的几乎完全相同。它工作得很好,尽管我对该解决方案创建的所有直接依赖关系不是100%满意。通过Spring等方式使用DI可能更好,但这并没有让我感到足够的困扰,无法实际使用解决方案

如果类有一个默认构造函数,只需调用
c.newInstance()
。如果没有,问题就要复杂一点——处理这个问题


当然,一旦你有了对象,你就需要对它们做些什么。标准的方法是让所有涉及的类实现同一个接口,然后你可以将对象向下转换到该接口,并对它们调用接口方法。(当然,在生产代码中,您需要捕获并处理所有可能出现的运行时异常,例如
实例化异常
非法访问异常
类卡斯特异常
)由于您正在迭代插件,我想所有插件的构造函数都有相同数量和类型的参数。(我不理解您的
插件。添加(…)
。)

无论如何,如果您想使用enum,我将执行以下操作,使用特定于常量的方法实现,而不是类型tokens/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 {
  //...
}
然后,您的循环将如下所示:

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

for (PluginTypes pt : PluginTypes.values()) {
    plugins.add(pt.create(400, 400));
}
List plugins=new ArrayList();
for(PluginTypes pt:PluginTypes.values()){
plugins.add(pt.create(400400));
}

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

你的第二个代码片段中有一个冲突,这让我有点困惑…变量
plugins
既用于Enum常量,也作为列表出现。所以我假设这一点,但你是应该发布代码片段,这些代码片段在将来实际工作

因此,是的,有一种方法可以满足您的需求:

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

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

要创建类的实例,您可以按照@Peter的答案,并保留对我建议的对象的引用

EnumMap-map=新的EnumMap(Plugins.class);
for(插件插件:Plugins.values()){
c类=plugins.getClassObject();
put(插件,c.newInstance());
}

为插件创建一个接口

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

    }
公共枚举插件{
旋转线(plugin.rotatingline.rotatingline.class),
SNOW_系统(plugin.SNOW.SnowSystem.class);

在这种情况下,私人课程的枚举将使你的生活更加困难

在过去,我是这样解决这个问题的:

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

}
类插件工厂{
私人课堂;
插件工厂(clazz类){
this.clazz=clazz;
}
T newInstance(){
返回clazz.newInstance();
}
最终静态插件工厂雪地系统工厂=
新插件工厂(SnowSystem.class);
...
//确实应该使用一个列表和一个由插件绑定的wilcard泛型,但它是
//就我的回答而言,太冗长了
最终静态插件工厂[]工厂={SNOW_系统工厂,…};
公共静态void main(字符串[]args){
Plugin[]arr=新插件[FACTORIES.length];
对于(int i=0;i
另一个选项是给newInstance var args对象参数。然后使用反射来找到将这些类型作为参数的适当构造函数。如果API的用户提供了一组错误的参数(引发异常),这将是非常混乱和完全不安全的

public T新实例(对象…args){
for(构造函数c:clazz.getConstructors()){
if(isMatchingConstructor(c,args)){
返回clazz.cast(c.newInstance(args));
}
}
抛出新的NoSuchMethodException(“未找到与参数匹配的构造函数:”
+toString(args));
}
私有布尔isMatchingConstructor(构造函数c、对象…参数){
类[]参数=c.getParameterTypes();
if(parameters.length!=args.length){
返回false;
}
对于(int i=0;i
有两种方法:

  • 使用
    Enum.valueOf()
    静态函数,然后将其强制转换为枚举类型

    Enum v = Enum.valueOf(TheEnumClass, valueInString);
    
  • 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);
        }
    
    }
    
    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;
    }
    
    Enum v = Enum.valueOf(TheEnumClass, valueInString);
    
    Plugins[] plugins = Plugins.class.getEnumConstants();
    for (Plugins plugin: plugins) {
        // use plugin.name() to get name and compare
    }