2012-03-22 142 views
6

初始化一個新對象,我有一個枚舉叫做插件:從類中枚舉

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插件的類的列表。

+1

哪裏列表來這裏?您無法動態地將值添加到枚舉。 – 2012-03-22 15:48:31

+0

被錯誤地命名爲相同。 plugins.add指的是一個名爲插件的列表,而插件是枚舉。 – 2012-03-22 19:27:14

回答

11

由於您正在迭代插件,我猜所有插件的構造函數都具有相同的編號&類型的參數。 (我不明白你的plugins.add(...)雖然)。

不管怎麼說,如果你想使用枚舉,我會做以下,使用定具體方法的實現,而不是類型的標記/反射

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

在另一方面,如果你知道插件你的實現類反正,爲什麼通過PluginType不使用它們,而不是直接Enum?

+0

這就是我所追求的答案,似乎最適合我的目的。非常感謝所有其他答案,這兩方面都學到了很多東西,並且給了我很多靈感! – 2012-03-22 18:34:03

+0

很高興我能幫到你。您可以通過接受答案來表明這一點;)如果您稍後改變主意,可以撤消/切換最適合您的答案。 – DaveFar 2012-03-22 18:42:20

+0

我是新來的,在之前沒有看到這個選項。現在完成,我希望(?)!再次感謝你。 – 2012-03-22 19:28:31

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

當然,你需要一些基本的異常處理,等等 - 這些代碼被銘刻在匆忙從我記得我在做什麼(:

+1

+1,或者你可以使用[ServiceLoader機制](http://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html) – Paaske 2012-03-22 15:58:08

+3

顯然,OP想要使用非默認的構造函數,所以'newInstance'不會直接工作。 – 2012-03-22 16:00:46

1

是的,你可以使用反射做到這一點。事實上,我們在項目的枚舉中做的幾乎完全一樣。它很好地工作,雖然我不是100%滿意這個解決方案創建的所有直接依賴關係。通過例如以某種方式使用DI可能更好。春天,但是這並沒有給我足夠的實際嘗試解決方案。

如果該類有一個默認的構造函數,只需調用c.newInstance()即可。如果沒有,這個問題有點複雜 - here is another post處理這個問題。

當然,一旦你有了物體,你需要對它們做些什麼。標準的方法是讓所有涉及的類都實現相同的接口,然後您可以將對象強制轉換爲該接口並在其上調用接口方法。 (當然,在生產代碼中,您需要捕獲並處理所有可能出現的運行時異常 - 例如InstantiationException,IllegalAccessExceptionClassCastException)。

0

有一個在你的第二個代碼片段一個衝突,混淆我有點...變量plugins既用於枚舉常量,當它出現一個列表。所以我假設這一點,但你應該發佈未來實際工作的代碼片段。

所以,是的,是有辦法做到你想要什麼:

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

此外,我建議你在看Service Loader,這是一個非常酷的工具,從classpath動態加載的服務。

+0

是的,在粘貼代碼的匆忙中,我將其重命名爲使其更加清晰,我偶然發現並命名了一些變體非常糟糕。抱歉。 – 2012-03-22 18:36:33

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

請注意,這是的僞代碼

2

枚舉在此situtation會讓你的生活更加困難。

在過去,我已經這樣解決了這個問題:

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變參對象參數。然後使用反射來查找將這些類型作爲參數的相應構造函數。如果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; 
} 
+0

不錯的使用工廠(+1)。你碰巧知道反射法查找的當前性能損失(使用JITing和不使用)?我經常看到完全不同的測量值,例如www.jguru.com/faq/view.jsp?EID=246569 – DaveFar 2012-03-22 17:19:26

+0

懲罰來自於必須對方法/構造函數/字段進行初始查找,例如'clazz.getConstructors()'。實際上通過反射調用方法/構造函數的開銷很小。所以如果你可以緩存返回的Methods/Constructors/Fields那麼性能命中可以忽略不計。 – Dunes 2012-03-22 17:30:07

+1

您鏈接的帖子是古老的(它是從java 1.3開始的!)。有一個閱讀這個http://stackoverflow.com/questions/414801/any-way-to-further-optimize-java-reflective-method-invocation – Dunes 2012-03-22 17:39:16

0

有2種方式:

  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 
    }