Initialize a new object from class in Enum

前端 未结 8 509
轻奢々
轻奢々 2021-01-03 00:27

I have an Enum called Plugins:

public enum Plugins {

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


        
相关标签:
8条回答
  • 2021-01-03 01:02

    There is a conflict in your second code snippet which confuses me a bit... the variable plugins is 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.

    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.

    0 讨论(0)
  • 2021-01-03 01:05

    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).

    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;
    }
    
    0 讨论(0)
  • 2021-01-03 01:09

    For creating instance of the class you can follow answer from @Peter and for holding the reference to the that object I suggest 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 讨论(0)
  • 2021-01-03 01:18

    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.)

    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?

    0 讨论(0)
  • 2021-01-03 01:22

    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 .xml or .properties (for instance) that will give you more flexibility:

    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 (:

    0 讨论(0)
  • 2021-01-03 01:24

    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.

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

    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, IllegalAccessException and ClassCastException).

    0 讨论(0)
提交回复
热议问题