Java 使用反射创建所有子类的对象列表是否合适?
在我的程序中,不同的功能被分为不同的模块,可能有数百个模块, 每个模块都是抽象模块类的一个子类 看起来像这样的Java 使用反射创建所有子类的对象列表是否合适?,java,reflection,Java,Reflection,在我的程序中,不同的功能被分为不同的模块,可能有数百个模块, 每个模块都是抽象模块类的一个子类 看起来像这样的 public abstract class Module { public final String name; public Module(String name){ this.name = name; } public abstract void execute(); } public class Mod1 extends M
public abstract class Module {
public final String name;
public Module(String name){
this.name = name;
}
public abstract void execute();
}
public class Mod1 extends Module{
public Mod1() {
super("Mod1");
}
@Override
public void execute() {
//Do something
}
}
有这样的子类
public abstract class Module {
public final String name;
public Module(String name){
this.name = name;
}
public abstract void execute();
}
public class Mod1 extends Module{
public Mod1() {
super("Mod1");
}
@Override
public void execute() {
//Do something
}
}
我需要列出所有模块的实例,但这样做有点乏味,因为我的程序可能有数百个模块,可能很难调试(我可能会错过一些lol)
private static final List MODULES=new ArrayList();
公共void init(){
MODULES.add(新Mod1());
MODULES.add(新Mod2());
MODULES.add(新Mod3());
MODULES.add(新Mod4());
MODULES.add(新Mod5());
}
因此,我认为使用反射可能是一种可行的方法,但在快速的谷歌搜索之后,我发现很多人不喜欢生产代码中的反射,所以我来这里询问,这是否是使用反射的适当情况,或者是否有任何理由或设计更改来避免在这种特殊情况下使用反射
编辑:该列表将用于gui中的渲染模块或从其他用户界面(如命令)调用模块的执行方法(只需找到具有匹配名称的模块并执行它)
注意:这并不是我的代码实际的样子,而是一个高度简化的版本,它给出了类似的想法对于一种最省力的方法,java提供了内置类 使用该类,您可以获得服务类(在本例中为
模块
)的所有可用实现,如
请注意,我强调了上一句中可用的术语。事实上,ServiceLoader
实现不执行任何高级类层次结构查找,而是依赖名为等于模块
类的规范名称的特定资源文件
META-INF/services/com.example.project.Module
com.example.project.impl1.Module1
com.example.project.impl2.Module2
其中,文件的每一行都引用了模块的可用实现
现在,ServiceLoader
是的一个实现,它不一定完全符合您的用例。然而,由于其服务加载和提供的思想相当简单,您可以轻松地构建自己的变体
public class JsonServiceLoader<S> {
private Class<S> service;
private Map<String, String> serviceIdentifiers;
public static <T> JsonServiceLoader load(Class<T> service, ClassLoader cl) {
Map<String, String> serviceIdentifiers = new HashMap<>();
String name = "META-INF/json-services/" + service.getCanonicalName();
// TODO check for null references where necessary
for (Enumeration<URL> resources = cl.getResources(name); resources.hasMoreElements();) {
try (InputStream resource = resources.next().openStream()) {
for (Map.Entry<String, String> identifier : parseJson(resource).entrySet()) {
serviceIdentifers.merge(
identifer.getKey(),
identifer.getValue(),
(value1, value2) -> throw new ServiceConfigurationError(
"duplicate service identifier '" + identifier.getKey() + "'"
);
);
}
}
}
return new JsonServiceLoader<>(service, serviceIdentifiers);
}
private static Map<String, String> parseJson(InputStream resource) {
// TODO parse JSON data from the given stream using your favourite JSON facility
/*
* If you want to use a different style of resources, e.g. XML, this is the only
* location you have to change (you might want to rename 'json-services' though).
*/
return new HashMap<>();
}
private JsonServiceLoader(Class<S> service, Map<String, String> serviceIdentifiers) {
this.service = service;
this.serviceIdentifiers = serviceIdentifiers;
}
public Set<String> getServiceIdentifiers() {
return Collections.unmodifiableSet(serviceIdentifiers.keySet());
}
public S getService(String name) {
String className = serviceIdenfiers.get(name);
if (null == className) {
throw new IllegalArgumentException("invalid service identifier '" + name + "'");
}
// TODO improve error handling
try {
return service.cast(Class.forName(className).newInstance());
} catch(Exception exc) {
throw new ServiceConfigurationError("could not load service '" + name + "'", exc);
}
}
}
允许将来扩展
此时,模块
类也不再需要知道它的名称(类成员名称
),因为您可以随时向服务加载程序请求适当的实例。(如果您在代码中的某个位置这样做,您将已经知道刚才询问的名称。)如果需要,您还可以向这个JsonServiceLoader
添加更多逻辑,例如缓存
最终,,这完全取决于您希望在模块
周围处理多少信息,希望模块
实现处理多少信息,以及希望服务框架处理多少信息。也许您可以将列表传递到父类的构造函数中,并将子类本身添加到构造函数方法中的列表。
就这样
public abstract class Module {
public final String name;
public Module(String name){
this.name = name;
}
public abstract void execute();
}
public class Mod1 extends Module{
public Mod1() {
super("Mod1");
}
@Override
public void execute() {
//Do something
}
}
公共抽象类模块{
公共最终字符串名;
公共模块(字符串名称、列表){
this.name=名称;
列表。添加(此);
}
公共抽象void execute();
}
private static final List MODULES=new ArrayList();
公共void init(){
新Mod1(模块);
}
这种特殊情况可以通过使用。然而,由于我们不知道“实例列表”的用途,这感觉它也可能是一个。该列表将用于不同类型的东西,如在gui中呈现模块或从其他用户界面(如命令)调用模块的执行方法(只需找到具有匹配名称的模块并执行它)这只会在模块#init
中的每行中保存几个字节的代码。然而,这个问题的目的是找到一种解决方案,它不依赖于修改模块
类来定位其实现。