如何使 Guice 正确绑定此泛型?
How do I make Guice bind this generic correctly?
我正在处理消息的平台上工作,我 运行 遇到了 Guice 绑定问题。我已经编写了一个示例项目来显示我遇到的问题,这是一个看起来像这样的异常:
1) com.icacarealign.worker_example.PreprocessorMiddleware<MessageType> cannot be used as a key; It is not fully specified.
代码来了
接口
public interface Middleware {
void configure();
}
public interface PreprocessorMiddleware<MessageType> extends Middleware {
void prework(Context<MessageType> contextObj);
}
public interface PostprocessorMiddleware<MessageType> extends Middleware {
void postwork(Context<MessageType> contextObj);
}
Guice 绑定
public class MiddlewareModule<MessageType> extends AbstractModule {
private final List<Class<? extends PostprocessorMiddleware<MessageType>>> _postprocessors;
private final List<Class<? extends PreprocessorMiddleware<MessageType>>> _preprocessors;
public MiddlewareModule(
List<Class<? extends PreprocessorMiddleware<MessageType>>> preprocessors,
List<Class<? extends PostprocessorMiddleware<MessageType>>> postprocessors
) {
_preprocessors = preprocessors;
_postprocessors = postprocessors;
}
@Override
protected void configure() {
bindAllMiddleware(_preprocessors, new TypeLiteral<PreprocessorMiddleware<MessageType>>() {});
bindAllMiddleware(_postprocessors, new TypeLiteral<PostprocessorMiddleware<MessageType>>() {});
}
private <T extends Middleware> void bindAllMiddleware(List<Class<? extends T>> middleware, TypeLiteral<T> type) {
Multibinder<T> multibinder = Multibinder.newSetBinder(binder(), type);
middleware.forEach(middlewareType -> bindMiddleware(multibinder, middlewareType));
}
private <T extends Middleware> void bindMiddleware(Multibinder<T> binder, Class<? extends T> type) {
binder().bind(type).in(Singleton.class);
binder.addBinding().to(type);
}
}
主要方法
public class Main {
public static void main(String[] args) {
List<Class<? extends PreprocessorMiddleware<Message>>> preprocessorMiddlewares = new ArrayList<>();
List<Class<? extends PostprocessorMiddleware<Message>>> postprocessorMiddlewares = new ArrayList<>();
preprocessorMiddlewares.add(ArbitraryPrepreprocessorMiddleware.class);
postprocessorMiddlewares.add(ArbitraryPostprocessorMiddleware.class);
MiddlewareModule<Message> module = new MiddlewareModule<>(preprocessorMiddlewares, postprocessorMiddlewares);
Injector injector = Guice.createInjector(module);
}
}
我做错了什么?
您不能出于 Guice 绑定的目的在 TypeLiteral
中使用类型变量(在本例中为 MessageType
)。相反,您需要重构以传递在不使用任何类型变量的情况下构造的 TypeToken
(可能这意味着从调用站点传入它)。
是对类似情况的更复杂的回答。结果是,如果你真的需要避免在每个调用点都创建类型字面量,你可以再抽象一点,但它的复杂性成本相当高。
正如 jacobm 所描述的,您需要解决泛型问题,并且可能需要传入您要绑定的类型。幸运的是,您可以使用 Guice 的 Types.newParameterizedType
来创建完全指定的 TypeLiteral。
public class MiddlewareModule<MessageType> extends AbstractModule {
private final Class<MessageType> _clazz;
private final List<Class<? extends PostprocessorMiddleware<MessageType>>> _postprocessors;
private final List<Class<? extends PreprocessorMiddleware<MessageType>>> _preprocessors;
public MiddlewareModule(
// Accept the message type in a way that survives erasure.
Class<MessageType> clazz,
List<Class<? extends PreprocessorMiddleware<MessageType>>> preprocessors,
List<Class<? extends PostprocessorMiddleware<MessageType>>> postprocessors
) {
_clazz = clazz;
_preprocessors = preprocessors;
_postprocessors = postprocessors;
}
@Override
protected void configure() {
// Use the Class to create your fully-specified TypeLiteral.
bindAllMiddleware(_preprocessors,
Types.newParameterizedType(PreprocessorMiddleware.class, _clazz));
bindAllMiddleware(_postprocessors,
Types.newParameterizedType(PostprocessorMiddleware.class, _clazz));
}
private <T extends Middleware> void bindAllMiddleware(List<Class<? extends T>> middleware, TypeLiteral<T> type) {
Multibinder<T> multibinder = Multibinder.newSetBinder(binder(), type);
middleware.forEach(middlewareType -> bindMiddleware(multibinder, middlewareType));
}
private <T extends Middleware> void bindMiddleware(Multibinder<T> binder, Class<? extends T> type) {
bind(type).in(Singleton.class); // Don't call binder() explicitly.
binder.addBinding().to(type);
}
}
请注意,这是未经测试的代码,因为我没有完整的 SSCCE;您可能需要调整类型参数,以便让 Guice 相信您在泛型中使用通配符是安全的。
我正在处理消息的平台上工作,我 运行 遇到了 Guice 绑定问题。我已经编写了一个示例项目来显示我遇到的问题,这是一个看起来像这样的异常:
1) com.icacarealign.worker_example.PreprocessorMiddleware<MessageType> cannot be used as a key; It is not fully specified.
代码来了
接口
public interface Middleware {
void configure();
}
public interface PreprocessorMiddleware<MessageType> extends Middleware {
void prework(Context<MessageType> contextObj);
}
public interface PostprocessorMiddleware<MessageType> extends Middleware {
void postwork(Context<MessageType> contextObj);
}
Guice 绑定
public class MiddlewareModule<MessageType> extends AbstractModule {
private final List<Class<? extends PostprocessorMiddleware<MessageType>>> _postprocessors;
private final List<Class<? extends PreprocessorMiddleware<MessageType>>> _preprocessors;
public MiddlewareModule(
List<Class<? extends PreprocessorMiddleware<MessageType>>> preprocessors,
List<Class<? extends PostprocessorMiddleware<MessageType>>> postprocessors
) {
_preprocessors = preprocessors;
_postprocessors = postprocessors;
}
@Override
protected void configure() {
bindAllMiddleware(_preprocessors, new TypeLiteral<PreprocessorMiddleware<MessageType>>() {});
bindAllMiddleware(_postprocessors, new TypeLiteral<PostprocessorMiddleware<MessageType>>() {});
}
private <T extends Middleware> void bindAllMiddleware(List<Class<? extends T>> middleware, TypeLiteral<T> type) {
Multibinder<T> multibinder = Multibinder.newSetBinder(binder(), type);
middleware.forEach(middlewareType -> bindMiddleware(multibinder, middlewareType));
}
private <T extends Middleware> void bindMiddleware(Multibinder<T> binder, Class<? extends T> type) {
binder().bind(type).in(Singleton.class);
binder.addBinding().to(type);
}
}
主要方法
public class Main {
public static void main(String[] args) {
List<Class<? extends PreprocessorMiddleware<Message>>> preprocessorMiddlewares = new ArrayList<>();
List<Class<? extends PostprocessorMiddleware<Message>>> postprocessorMiddlewares = new ArrayList<>();
preprocessorMiddlewares.add(ArbitraryPrepreprocessorMiddleware.class);
postprocessorMiddlewares.add(ArbitraryPostprocessorMiddleware.class);
MiddlewareModule<Message> module = new MiddlewareModule<>(preprocessorMiddlewares, postprocessorMiddlewares);
Injector injector = Guice.createInjector(module);
}
}
我做错了什么?
您不能出于 Guice 绑定的目的在 TypeLiteral
中使用类型变量(在本例中为 MessageType
)。相反,您需要重构以传递在不使用任何类型变量的情况下构造的 TypeToken
(可能这意味着从调用站点传入它)。
正如 jacobm 所描述的,您需要解决泛型问题,并且可能需要传入您要绑定的类型。幸运的是,您可以使用 Guice 的 Types.newParameterizedType
来创建完全指定的 TypeLiteral。
public class MiddlewareModule<MessageType> extends AbstractModule {
private final Class<MessageType> _clazz;
private final List<Class<? extends PostprocessorMiddleware<MessageType>>> _postprocessors;
private final List<Class<? extends PreprocessorMiddleware<MessageType>>> _preprocessors;
public MiddlewareModule(
// Accept the message type in a way that survives erasure.
Class<MessageType> clazz,
List<Class<? extends PreprocessorMiddleware<MessageType>>> preprocessors,
List<Class<? extends PostprocessorMiddleware<MessageType>>> postprocessors
) {
_clazz = clazz;
_preprocessors = preprocessors;
_postprocessors = postprocessors;
}
@Override
protected void configure() {
// Use the Class to create your fully-specified TypeLiteral.
bindAllMiddleware(_preprocessors,
Types.newParameterizedType(PreprocessorMiddleware.class, _clazz));
bindAllMiddleware(_postprocessors,
Types.newParameterizedType(PostprocessorMiddleware.class, _clazz));
}
private <T extends Middleware> void bindAllMiddleware(List<Class<? extends T>> middleware, TypeLiteral<T> type) {
Multibinder<T> multibinder = Multibinder.newSetBinder(binder(), type);
middleware.forEach(middlewareType -> bindMiddleware(multibinder, middlewareType));
}
private <T extends Middleware> void bindMiddleware(Multibinder<T> binder, Class<? extends T> type) {
bind(type).in(Singleton.class); // Don't call binder() explicitly.
binder.addBinding().to(type);
}
}
请注意,这是未经测试的代码,因为我没有完整的 SSCCE;您可能需要调整类型参数,以便让 Guice 相信您在泛型中使用通配符是安全的。