通用接口转换
Generic Interface Casting
我正在努力创建一个非常简单的 OS 类框架(类似于 Android),该框架基于 JVM 构建,用于教育目的。
主要是,我只是想了解 ClassLoader
以及它们的无限可能性。我已经创建了大部分基础设施,但我质疑是否有更好的方法来处理我所处情况的泛型。
关于我的框架运行方式的一些描述:
"App
" 我框架中的实现能够附加扩展,即插件。扩展系统的工作原理是要求扩展实现 App
代码知道的接口,但是 App
代码根本不知道扩展,它只知道接口。
这是我使用 ClassLoader
s 的地方。我使用 URLClassLoader
递归加载 $ApplicationRoot/ext
目录中的所有 .class
文件并测试查看加载的 classes)
.
中的接口 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 信息的 Class
变量的名称,而不是实际类型,这将不起作用。
我的直觉是我将不得不接受它并使用 List<Object>
然后不安全地将其丢弃,但我希望其他人可能有更好的建议。
有几个想法不太适合发表评论:
立即想到的是,您可以将 Class
传递给扩展加载例程,每个加载的 classes 都应该是其子类型。
例如:
public <E> Map<Class<? extends E>, List<E>> loadExtensions
(Class<E> extType,
List<String> extNames) {
// ...;
}
但是,这仅在某些地方静态地知道 class 每个扩展名应该是其子类型的情况下才有效。扩展加载可能会被委托给应用程序实现。例如,他们最终可能会做如下事情:
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()
idiom or TypeToken
,而不是传递 class。
介于#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
扩展名。
无论如何,似乎无论您决定做什么,都需要有人静态地知道他们的扩展的超类型是什么。
我正在努力创建一个非常简单的 OS 类框架(类似于 Android),该框架基于 JVM 构建,用于教育目的。
主要是,我只是想了解 ClassLoader
以及它们的无限可能性。我已经创建了大部分基础设施,但我质疑是否有更好的方法来处理我所处情况的泛型。
关于我的框架运行方式的一些描述:
"App
" 我框架中的实现能够附加扩展,即插件。扩展系统的工作原理是要求扩展实现 App
代码知道的接口,但是 App
代码根本不知道扩展,它只知道接口。
这是我使用 ClassLoader
s 的地方。我使用 URLClassLoader
递归加载 $ApplicationRoot/ext
目录中的所有 .class
文件并测试查看加载的 classes)
.
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 信息的 Class
变量的名称,而不是实际类型,这将不起作用。
我的直觉是我将不得不接受它并使用 List<Object>
然后不安全地将其丢弃,但我希望其他人可能有更好的建议。
有几个想法不太适合发表评论:
立即想到的是,您可以将
Class
传递给扩展加载例程,每个加载的 classes 都应该是其子类型。例如:
public <E> Map<Class<? extends E>, List<E>> loadExtensions (Class<E> extType, List<String> extNames) { // ...; }
但是,这仅在某些地方静态地知道 class 每个扩展名应该是其子类型的情况下才有效。扩展加载可能会被委托给应用程序实现。例如,他们最终可能会做如下事情:
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()
idiom orTypeToken
,而不是传递 class。介于#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
扩展名。
无论如何,似乎无论您决定做什么,都需要有人静态地知道他们的扩展的超类型是什么。