【问题标题】:Generic Interface Casting通用接口铸造
【发布时间】:2017-06-08 16:15:22
【问题描述】:

我正致力于创建一个非常简单的类似操作系统的框架(类似于 Android),它基于 JVM 构建,用于教育目的。

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

对我的框架运行方式的一点描述:

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

这就是我使用ClassLoaders 的地方。我使用URLClassLoader 以递归方式加载$ApplicationRoot/ext 目录中的所有.class 文件并测试是否从加载的接口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&lt;Object&gt;

我真正想做的是这样的:

private Map<Class<?>, List<iface>> extensions;

鉴于iface 是保存类信息的Class 变量的名称,而不是实际的类型,这将不起作用。

我的直觉是,我将不得不接受它并使用List&lt;Object&gt; 并且不安全地将其丢弃,但我希望其他人可能有更好的建议。

【问题讨论】:

  • App 知道扩展超级接口,所以每个扩展都应该实现那个接口,对吧?或者您是否试图将类加载例程推广到任意种类的类文件?
  • @Radiodef 正是后者
  • @Radiodef 每个App 都有自己的prop.xml 文件,该文件具有extension 标记,用于拼出每种扩展类型的接口名称。 extensionInterfaceNames List 是从 xml 输入加载器的数据。这个想法是,一旦App 完全加载,它就可以根据接口名称检索扩展列表。假设App 是一个具有一些内置功能的计算器,但也许在线商店可以提供附加功能的下载链接。可能有一个Calculation 接口,所有这些附加组件都实现了
  • Calculation 接口将特定于 App
  • 关闭选民和反对选民是否愿意分享他们决定背后的理由?还是您真的不关心网站的完整性,只是盲目地点击反对票和关闭投票按钮?

标签: java generics interface casting classloader


【解决方案1】:

一些不太适合评论的想法:

  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&lt;Class&lt;?&gt;, List&lt;Object&gt;&gt; 然后让它们可以按类型检索:

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

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

【讨论】:

  • 我很可能会尝试更改您的第一个建议的版本。感谢您的时间和精力。我也想让你知道,我不是对你的答案投反对票的人。
  • 我看到他们也对你的问题投了反对票。谁知道,真的。
  • 我投了赞成票,只是因为有人投了反对票。谁真正知道任何事情的真相。
猜你喜欢
  • 2012-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多