I'm working on a platform for processing messages, and I'm running into a problem with Guice bindings. I've written a sample project that displays the problem I'm having, which is an exception that looks like this:
1) com.icacarealign.worker_example.PreprocessorMiddleware<MessageType> cannot be used as a key; It is not fully specified.
Here comes the code.
The Interfaces
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);
}
The Guice Binding
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);
}
}
The Main Method
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);
}
}
What am I doing wrong?
As jacobm describes, you need to work around the generics, and will probably need to pass in the type you're binding. Luckily, you can use Guice's Types.newParameterizedType
to create your fully-specified 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);
}
}
Note that this is untested code, since I don't have a full SSCCE; you may need to tweak the type parameters in order to convince Guice that your use of wildcards in generics is safe.