【问题标题】:Enum-aware ServiceLoader implementation?枚举感知的 ServiceLoader 实现?
【发布时间】:2019-02-16 04:00:41
【问题描述】:

我希望能够将枚举类型指示为接口实现,然后通过ServiceLoader API 将所有枚举加载为接口的单独实例/实现。此用例的一个示例是允许我的 API 的下游用户指定自定义值,但提供具有标准/通用实现的枚举。我的接口只需要一个字符串name(),所以任何枚举都已经实现了。

例如,Java NIO API 中的 CopyOption 接口,以及提供的 StandardCopyOption 枚举。假设我想通过ServiceLoader 将所有CopyOptions,甚至是类路径上的新内容以及标准加载到单个迭代器中(或者我愿意接受其他建议!)

我终于通过批量复制ServiceLoader 并修改它以在实例化失败时尝试使用getEnumConstants 来让它工作(try 中的部分是它目前的工作方式,catch 中的部分是什么我添加/更改):

try {
    S p = service.cast(c.newInstance());
    providers.put(cn, p);
    return p;
} catch (Throwable x) {
    Object[] arr = c.getEnumConstants();
    if (arr == null || arr.length == 0) {
        fail(service, "Provider " + cn + " could not be instantiated", x);  
    }

    List<S> list = new LinkedList<>();
    for (Object o : arr) {
        Enum<?> e = (Enum<?>) o;
        S p = service.cast(e);
        providers.put(cn + e.ordinal(), p);
        list.add(p);
    }
    subiter = list.iterator();
    return subiter.next();
}

我还添加了一些代码,如果subiter 存在并且有下一个,则在继续下一个类名之前对其进行迭代。

我的问题是:有没有更好的方法?

如果最终用途不明确,现在可以通过上述修改:

interface ImageType {
    String name();
}

@AutoService(ImageType.class)
enum StandardImageType implements ImageType {
    IMAGE,
    VECTOR,
    RASTER,
    HANDWRITING,
    ICON,
    LOGO,
    SEAL,
    RULE,
    BARCODE
}

【问题讨论】:

    标签: java enums serviceloader


    【解决方案1】:

    随着 Java 模块的引入,an alternative 通过默认构造函数进行实例化已被添加到服务提供者中。但它仅在提供程序位于命名模块中时才有效。

    提供者类可以声明public static T provider() 方法,其中T 是服务类型。然后,提供者实现类甚至不需要自己实现或扩展T

    由于数组和像List&lt;ImageType&gt; 这样的泛型类型都不能用作服务类型,我们需要另一种类型来潜在地封装多个实际实例,例如

    package somemodule;
    
    import java.util.function.Supplier;
    
    public interface ImageType {
        String name();
        interface ImageTypes extends Supplier<ImageType[]> {}
    }
    

    package somemodule;
    
    public enum StandardImageType implements ImageType {
        IMAGE,
        VECTOR,
        RASTER,
        HANDWRITING,
        ICON,
        LOGO,
        SEAL,
        RULE,
        BARCODE;
    
        public static ImageTypes provider() {
            return StandardImageType::values;
        }
    }
    

    和类似的模块声明

    module SomeModule {
        uses somemodule.ImageType.ImageTypes;
        provides somemodule.ImageType.ImageTypes with somemodule.StandardImageType;
    }
    

    允许写,例如

    List<ImageType> all = ServiceLoader.load(ImageType.ImageTypes.class)
            .stream().flatMap(p -> Arrays.stream(p.get().get()))
            .collect(Collectors.toList());
    

    模块中的某处(或任何其他带有uses somemodule.ImageType.ImageTypes; 声明的模块)。

    【讨论】:

    • 哇!好消息-谢谢!并确认:这仍然会通过“正常”实现来获取/迭代,例如@AutoService(ImageType.class) class MyCustomImageType 实现 ImageType { ... 除了通过显式提供者提供的那些?
    • 这取决于实现@AutoService 功能的框架。如前所述,新功能仅适用于已声明的模块,因此框架必须生成provides 声明(或将其编译后的等效项)注入模块信息。将类名写入 META-INF/services 中的文件将不适用于模块。
    • 最后一句话回答了我的问题,谢谢。我指的是 Google 的 @AutoService 注释以方便维护 META-INF/services 但这就是我的问题所在
    • @Dragas 它在 Netbeans 中工作,the JLS 明确表示:“另外,请注意,当服务提供者声明提供者方法时,服务提供者本身不必是服务的子类型.
    • @Dragas 我在回答中暗示了这一点,当时我写了“但它仅在提供程序位于命名模块中时才有效”......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-23
    • 1970-01-01
    • 1970-01-01
    • 2015-05-30
    • 1970-01-01
    • 1970-01-01
    • 2014-08-18
    相关资源
    最近更新 更多