通用接口转换

时间:2017-06-08 16:15:23

标签: java generics interface casting classloader

我正在努力创建一个非常简单的类似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>并且不安全地将其抛弃,但我希望别人可能有更好的建议。

1 个答案:

答案 0 :(得分:2)

一些不容易纳入评论的想法:

  1. 您可以立即想到的是,您可以将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, ...);
    }
    
  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()习语或TypeToken,而不是传递一个类。

  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扩展名。

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