通用接口转换

Generic Interface Casting

我正在努力创建一个非常简单的 OS 类框架(类似于 Android),该框架基于 JVM 构建,用于教育目的。

主要是,我只是想了解 ClassLoader 以及它们的无限可能性。我已经创建了大部分基础设施,但我质疑是否有更好的方法来处理我所处情况的泛型。

关于我的框架运行方式的一些描述:

"App" 我框架中的实现能够附加扩展,即插件。扩展系统的工作原理是要求扩展实现 App 代码知道的接口,但是 App 代码根本不知道扩展,它只知道接口。

这是我使用 ClassLoaders 的地方。我使用 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> 然后不安全地将其丢弃,但我希望其他人可能有更好的建议。

有几个想法不太适合发表评论:

  1. 立即想到的是,您可以将 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, ...);
    }
    
  2. 您也可以将所有扩展转储到例如你的 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());
        }
    }
    
  3. 另一种我不太喜欢但值得考虑的方式是 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。

  4. 介于#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 扩展名。

无论如何,似乎无论您决定做什么,都需要有人静态地知道他们的扩展的超类型是什么。