Enum-aware ServiceLoader implementation?

泪湿孤枕 提交于 2020-05-12 06:57:21

问题


I would like to be able to indicate an enum type as an interface implementation and then load all enums as separate instances/implementations of the interface via the ServiceLoader API. An example of this use case would be to allow downstream users of my API to specify custom values, but provide an enum with standard/common implementations. My interface only requires a String name(), so any enum implements it already.

For example, the CopyOption interface in the Java NIO APIs, with the provided StandardCopyOption enum. Say I wanted to load all CopyOptions, even new ones on the classpath along with the standards, to a single iterator via ServiceLoader (or I'm open to other suggestions!)

I finally got it to work by wholesale copying ServiceLoader and modifying it to try using getEnumConstants when instantiation fails (the part in the try is how it works currently and the part in the catch is what I added/changed):

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();
}

I also added some code such that if subiter exists and has next, it is iterated before moving on to the next class name.

My question is: Is there a better way?

In case the end use is not clear, this is now what's possible with the above modifications:

interface ImageType {
    String name();
}

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

回答1:


With the introduction of Java modules, an alternative to instantiating via default constructor has been added to service providers. But it only work when the provider is in a named module.

The provider class may declare a public static T provider() method where T is the service type. Then, the provider implementation class doesn’t even need to implement or extend T itself.

Since neither, arrays nor a generic type like List<ImageType> can be used as service type, we would need another type for potentially encapsulating multiple actual instances, e.g.

package somemodule;

import java.util.function.Supplier;

public interface ImageType {
    String name();
    interface ImageTypes extends Supplier<ImageType[]> {}
}

and

package somemodule;

public enum StandardImageType implements ImageType {
    IMAGE,
    VECTOR,
    RASTER,
    HANDWRITING,
    ICON,
    LOGO,
    SEAL,
    RULE,
    BARCODE;

    public static ImageTypes provider() {
        return StandardImageType::values;
    }
}

and a module declaration like

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

which allows to write, e.g.

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

somewhere within the module (or any other module with a uses somemodule.ImageType.ImageTypes; declaration).



来源:https://stackoverflow.com/questions/54719780/enum-aware-serviceloader-implementation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!