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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9825708/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-16 08:10:57  来源:igfitidea点击:

Initialize a new object from class in Enum

javaenums

提问by jnsstnbrg

I have an Enum called Plugins:

我有一个名为 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;
    }

}

What I would like to do is to loop through all the enums in Pluginsand create new objects from those using the variable clike this:

我想要做的是遍历所有枚举Plugins并从使用变量的那些创建新对象,c如下所示:

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

Is there a way of accomplishing this with a similar method? The reason why I want to do this is to create a list of classes that should be added to the List plugins when I start my application.

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

采纳答案by DaveFar

Since you are iterating over the plugins, I guess all plugins' constructors have the same number & type of parameters. (I do not understand your plugins.add(...)though.)

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

Anyways, if you want to use Enums, I would do the following, using constant-specific method implementations instead of type 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 {
  //...
}

Then your loop would look something like this:

然后你的循环看起来像这样:

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

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

On the other hand, if you know your implementation classes of Plugin anyways, why not use them directly instead of via the PluginTypes Enum?

另一方面,如果你知道你的 Plugin 实现类,为什么不直接使用它们而不是通过 PluginTypes 枚举?

回答by Marcelo

First, you need all the classes to implement some kind of interface:

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

public interface Plugin {
    public doSomething();
}

Then you can do

然后你可以做

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

About loading your plugins, you can either declare all of them in an enum... or you can declare them in a configuration .xmlor .properties(for instance) that will give you more flexibility:

有关加载你的插件,你可以宣布所有的人都在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());
}

Of course, you'll need some basic exception handling, etc - this code's been written in a hurry from what I remember doing (:

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

回答by Péter T?r?k

Yes, you can do it using reflection. In fact, we are doing almost exactly the same within an enum in our project. It works nicely, although I am not 100% comfortable with all the direct dependencies this solution creates. It may be better to somehow use DI via e.g. Spring, however this hasn't bugged me enough to actually experiment with a solution.

是的,您可以使用反射来做到这一点。事实上,我们在项目中的枚举中所做的几乎完全相同。它工作得很好,虽然我对这个解决方案创建的所有直接依赖关系不是 100% 满意。以某种方式通过例如 Spring 以某种方式使用 DI 可能会更好,但是这并没有让我真正尝试解决方案。

If the class has a default constructor, simply call c.newInstance(). If not, the issue is a bit more complicated - here is another postdealing with this.

如果类有默认构造函数,只需调用c.newInstance(). 如果没有,问题就有点复杂了 -这是另一篇处理这个问题的帖子

Of course, once you have the objects, you need to do something with them. The standard way is to have all classes involved implement the same interface, then you can cast down the objects to that interface and call interface methods on them. (Of course, in production code, you need to catch and handle all possible runtime exceptions which might arise - such as InstantiationException, IllegalAccessExceptionand ClassCastException).

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

回答by Michael Schmei?er

There is a conflict in your second code snippet which confuses me a bit... the variable pluginsis used both for enum constants and as a list it appears. So I am assuming this, but you should post code snippets which actually work in future.

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

So, yes, there is a way to do what you want:

所以,是的,有一种方法可以做你想做的事:

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

Also I recommend you looking at Service Loader, which is a really cool tool to dynamically load services from the classpath.

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

回答by Jaydeep Patel

For creating instance of the class you can follow answer from @Peter and for holding the reference to the that object I suggest EnumMap.

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

回答by Kevin Bowersox

Create an interface for the plugins

为插件创建一个接口

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

Create a method on the enum that creates an instance of 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;
        }

    }

Instantiate in loop

在循环中实例化

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

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

Please note this is Psuedo Code

请注意这是伪代码

回答by Dunes

Enums in this situtation will make your life more difficult.

在这种情况下,枚举会使您的生活更加困难。

In the past I've solved this problem this way:

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

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

}

The other option is to give newInstance var args object parameter. Then use reflections to find the appropriate constructor that takes these types as parameters. This is hideously messy and completely unsafe if a user of the API gives a bad set of arguments (exception thrown).

另一种选择是给 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;
}

回答by Mavlarn

There are 2 ways:

有2种方式:

  1. use Enum.valueOf()static function, then cast it into your enum type.

    Enum v = Enum.valueOf(TheEnumClass, valueInString);
    
  2. Use class.getEnumConstants()function to get the list of the enum constants, and loop this list and get.

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