我有一个名为Plugins的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插件。
答案 0 :(得分:11)
由于你在迭代插件,我想所有插件的构造函数都有相同的数字和&amp;参数类型。 (我不明白你的plugins.add(...)
。)
无论如何,如果你想使用Enums,我会使用常量特定的方法实现而不是类型标记/反射来执行以下操作:
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));
}
另一方面,如果您知道Plugin的实现类,为什么不直接使用它们而不是通过PluginTypes Enum?
答案 1 :(得分: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));
}
请注意这是Psuedo Code
答案 2 :(得分: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());
}
当然,你需要一些基本的异常处理等等 - 这段代码是我记得做的匆忙写的(:
答案 3 :(得分:2)
这种情况下的枚举会让你的生活变得更加艰难。
过去我用这种方式解决了这个问题:
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 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;
}
答案 4 :(得分:1)
是的,您可以使用反射来完成。事实上,我们在项目的枚举中几乎完全相同。它工作得很好,虽然我对这个解决方案创建的所有直接依赖关系并不是很满意。以某种方式使用DI可能更好春天,然而这并没有让我足够实际尝试解决方案。
如果类有默认构造函数,只需调用c.newInstance()
即可。如果没有,问题就更复杂了 - here is another post处理这个问题。
当然,一旦你拥有了这些物体,你需要对它们做些什么。标准方法是让所有涉及的类实现相同的接口,然后您可以将对象转换为该接口并在其上调用接口方法。 (当然,在生产代码中,您需要捕获并处理可能出现的所有可能的运行时异常 - 例如InstantiationException
,IllegalAccessException
和ClassCastException
)。
答案 5 :(得分:0)
您的第二个代码段中存在冲突,这让我感到困惑......变量plugins
既用于枚举常量,也用作列表。所以我假设这一点,但您应该发布实际上将来可用的代码片段。
所以,是的,有办法做你想做的事:
for (Plugins plugins : Plugins.values()) {
Class<?> c = plugins.getClassObject();
pluginsList.add(c.getConstructor(Integer.TYPE, Integer.TYPE).newInstance(400, 400));
}
我还建议您查看Service Loader,这是一个非常酷的工具,可以从类路径动态加载服务。
答案 6 :(得分: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());
}
答案 7 :(得分:0)
有两种方式:
使用Enum.valueOf()
静态函数,然后将其强制转换为枚举类型。
Enum v = Enum.valueOf(TheEnumClass, valueInString);
使用class.getEnumConstants()
函数获取枚举常量列表,并循环此列表并获取。
Plugins[] plugins = Plugins.class.getEnumConstants();
for (Plugins plugin: plugins) {
// use plugin.name() to get name and compare
}