我正在努力创建一个非常简单的类似OS的框架(类似于Android),它基于JVM构建,用于教育目的。
大多数情况下,我只是想了解ClassLoader
以及它们的无限可能性。我创建了大部分基础设施,但我质疑是否有更好的方法来处理我所处的情况的泛型。
对我的框架运作方式的一点描述:
" App
"我的框架中的实现能够附加扩展,即插件。扩展系统通过要求扩展来实现App
代码知道的接口,但App
代码根本不知道扩展,它只知道接口。
这是我使用ClassLoader
的地方。我使用URLClassLoader
递归地加载.class
目录中的所有$ApplicationRoot/ext
文件并测试是否加载的类isAssignable(
中的接口)
。
除非我尝试进一步概括它,否则这一切都很好用。我想要做的是拥有可以处理所有这些的SDK代码,以便应用程序只需要确保正确的扩展名在正确的文件夹中。
现在这是我的代码:
Set<String> classFiles = new HashSet<>();
gatherAllClassFiles("", classFiles, srcRoot);
gatherAllClassFiles("", classFiles, extRoot);
URLClassLoader ucl = new URLClassLoader(new URL[]{extRoot.toURI().toURL(), srcRoot.toURI().toURL()});
for(String className:classFiles){
Class<?> clazz = ucl.loadClass(className);
for(String extension: extensionInterfaceNames){
Class<?> iface = Class.forName(extension);
if(iface.isAssignableFrom(clazz)){
extensions.putIfAbsent(iface, new ArrayList<>());
extensions.get(iface).add(iface.cast(clazz.newInstance()));
}
}
}
extensions
声明如下:
private Map<Class<?>, List<?>> extensions;
我知道泛型的这种实现并不是最不理想的,事实上,在目前的状态下,它不会编译,但我真的想远离使用List<Object>
。
我真正希望能做的是这样的事情:
private Map<Class<?>, List<iface>> extensions;
假设iface
是保存类信息而不是实际类型的Class
变量的名称,这将不起作用。
我的直觉是,我将不得不吮吸它并使用List<Object>
并且不安全地将其抛弃,但我希望别人可能有更好的建议。
答案 0 :(得分:2)
一些不容易纳入评论的想法:
您可以立即想到的是,您可以将Class
传递给扩展加载例程,每个加载的类应该是一个子类型。
例如:
public <E> Map<Class<? extends E>, List<E>> loadExtensions
(Class<E> extType,
List<String> extNames) {
// ...;
}
然而,这只有在某些地方确实知道每个扩展应该是子类的类的地方时才有效。可能会将扩展加载委托给应用程序实现。例如,他们最终可能会做类似的事情:
class CalculatorApp extends App {
Map<Class<? extends Calculation>, List<Calculation>> extensions =
loadExtensions(Calculation.class, ...);
}
您也可以将所有扩展程序转储到例如您的Map<Class<?>, List<Object>>
,然后按类型检索它们:
abstract class App {
private final Map<Class<?>, List<Object>> extensions =
loadExtensions();
protected final <E> List<E> findExtensions(Class<E> extType) {
return extensions.entrySet().stream()
.filter(e -> extType.isAssignableFrom(e.getKey()))
.flatMap(e -> e.getValue().stream())
.map(extType::cast)
.collect(Collectors.toList());
}
}
另一种我不喜欢但值得考虑的方法是App
带有扩展类型的类型参数:
abstract class App<E> {
protected final Class<E> extType;
protected final Map<Class<? extends E>, List<E>> extensions;
protected App(Class<E> extType) {
this.extType = extType;
this.extensions = loadExtensions(extType);
}
}
然后它实现为:
class CalculatorApp extends App<Calculation> {
CalculatorApp() { super(Calculation.class); }
}
在这种情况下,你也可以使用getGenericSuperclass().getActualTypeArguments()
习语或TypeToken
,而不是传递一个类。
#1和#3之间的交叉,你可以将扩展抽象到一个单独的容器中:
class Extensions<E> {
private final Class<E> type;
private final List<E> extensions;
public Extensions(Class<E> type) {
this.type = type;
this.extensions = App.findExtensions(type);
}
}
这将允许更容易实现,例如使用多种扩展类型。对于计算器示例,他们可能希望同时具有Calculation
扩展名,例如NumberDisplayFormat
扩展名。