枚举感知 ServiceLoader 实现?
Enum-aware ServiceLoader implementation?
我希望能够将枚举类型指示为接口实现,然后通过 ServiceLoader
API 将所有枚举作为接口的单独 instances/implementations 加载。此用例的一个示例是允许我的 API 的下游用户指定自定义值,但提供具有 standard/common 实现的枚举。我的接口只需要一个字符串 name()
,所以任何枚举都已经实现了它。
例如,Java NIO API 中的 CopyOption
接口,带有提供的 StandardCopyOption
枚举。假设我想通过 ServiceLoader
将所有 CopyOption
加载到单个迭代器(或者我愿意接受其他建议!)
我最终通过批量复制 ServiceLoader
并修改它以在实例化失败时尝试使用 getEnumConstants
来让它工作(try
中的部分是它当前的工作方式以及catch
中的部分是我 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();
}
我还添加了一些代码,如果 subiter
存在并且有下一个,则在移动到下一个 class 名称之前对其进行迭代。
我的问题是:有没有更好的办法?
如果最终用途不清楚,现在可以通过上述修改实现:
interface ImageType {
String name();
}
@AutoService(ImageType.class)
enum StandardImageType implements ImageType {
IMAGE,
VECTOR,
RASTER,
HANDWRITING,
ICON,
LOGO,
SEAL,
RULE,
BARCODE
}
随着 Java 模块的引入,an alternative 通过默认构造函数进行实例化已添加到服务提供者中。但它仅在提供程序位于命名模块中时才有效。
提供商 class 可以声明一个 public static T provider()
方法,其中 T
是服务类型。然后,提供者实现 class 甚至不需要实现或扩展 T
本身。
因为数组和像 List<ImageType>
这样的泛型类型都不能用作服务类型,我们需要另一种类型来潜在地封装多个实际实例,例如
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;
声明的任何其他模块)。
我希望能够将枚举类型指示为接口实现,然后通过 ServiceLoader
API 将所有枚举作为接口的单独 instances/implementations 加载。此用例的一个示例是允许我的 API 的下游用户指定自定义值,但提供具有 standard/common 实现的枚举。我的接口只需要一个字符串 name()
,所以任何枚举都已经实现了它。
例如,Java NIO API 中的 CopyOption
接口,带有提供的 StandardCopyOption
枚举。假设我想通过 ServiceLoader
将所有 CopyOption
加载到单个迭代器(或者我愿意接受其他建议!)
我最终通过批量复制 ServiceLoader
并修改它以在实例化失败时尝试使用 getEnumConstants
来让它工作(try
中的部分是它当前的工作方式以及catch
中的部分是我 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();
}
我还添加了一些代码,如果 subiter
存在并且有下一个,则在移动到下一个 class 名称之前对其进行迭代。
我的问题是:有没有更好的办法?
如果最终用途不清楚,现在可以通过上述修改实现:
interface ImageType {
String name();
}
@AutoService(ImageType.class)
enum StandardImageType implements ImageType {
IMAGE,
VECTOR,
RASTER,
HANDWRITING,
ICON,
LOGO,
SEAL,
RULE,
BARCODE
}
随着 Java 模块的引入,an alternative 通过默认构造函数进行实例化已添加到服务提供者中。但它仅在提供程序位于命名模块中时才有效。
提供商 class 可以声明一个 public static T provider()
方法,其中 T
是服务类型。然后,提供者实现 class 甚至不需要实现或扩展 T
本身。
因为数组和像 List<ImageType>
这样的泛型类型都不能用作服务类型,我们需要另一种类型来潜在地封装多个实际实例,例如
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;
声明的任何其他模块)。