Inyectar una implementación de un subtipo de interfaz genérica usando Guice
Frecuentes
Visto 993 equipos
3
I have the following APIs defined:
public interface Input<I> {
Collection<? extends I> read();
}
public interface Transformer<O, I> {
Collection<? extends O> transform(Collection<? extends I> inputData);
}
public interface Output<O> {
void write(Collection<? extends O> output);
}
public interface Executor {
void execute();
}
and the following implementations:
public final class InputImpl implements Input<String> {
@Override
public Collection<? extends String> read() {
return asList("1", "2");
}
}
public final class TransformerImpl implements Transformer<Integer, String> {
@Override
public Collection<? extends Integer> transform(final Collection<? extends String> inputData) {
return inputData.stream().map(Integer::parseInt).collect(Collectors.toList());
}
}
public final class OutputImpl implements Output<Integer> {
@Override
public void write(final Collection<? extends Integer> output) {
System.out.println(output);
}
}
public final class ExecutorImpl<I, O> implements Executor {
private final Input<I> input;
private final Transformer<O, I> transformer;
private final Output<O> output;
@Inject
public ExecutorImpl(final Input<I> input, final Transformer<O, I> transformer, final Output<O> output) {
this.input = input;
this.transformer = transformer;
this.output = output;
}
public void execute() {
final Collection<? extends I> inputData = input.read();
final Collection<? extends O> outputData = transformer.transform(inputData);
output.write(outputData);
}
}
Here is my attempt to bind the APIs to the implementations from above:
public final class ModuleImpl extends AbstractModule {
@Override
protected void configure() {
bind(new TypeLiteral<Input<String>>() {
}).to(InputImpl.class);
bind(new TypeLiteral<Transformer<Integer, String>>() {
}).to(TransformerImpl.class);
bind(new TypeLiteral<Output<Integer>>() {
}).to(OutputImpl.class);
bind(Executor.class).to(ExecutorImpl.class);
}
}
When I am trying to get an instance of Executor
using a Guice injector, I am getting the following errors:
1) com.csc.playground.guice.api.Input<I> cannot be used as a key; It is not fully specified.
at com.csc.playground.guice.ExecutorImpl.<init>(ExecutorImpl.java:25)
at com.csc.playground.guice.module.ModuleImpl.configure(ModuleImpl.java:33)
2) com.csc.playground.guice.api.Transformer<O, I> cannot be used as a key; It is not fully specified.
at com.csc.playground.guice.ExecutorImpl.<init>(ExecutorImpl.java:25)
at com.csc.playground.guice.module.ModuleImpl.configure(ModuleImpl.java:33)
3) com.csc.playground.guice.api.Output<O> cannot be used as a key; It is not fully specified.
at com.csc.playground.guice.ExecutorImpl.<init>(ExecutorImpl.java:25)
at com.csc.playground.guice.module.ModuleImpl.configure(ModuleImpl.java:33)
1 Respuestas
3
Conceptually, the problem is here:
@Inject
public ExecutorImpl(final Input<I> input,
final Transformer<O, I> transformer,
final Output<O> output) {
When guice attempts to fulfill the dependency for, say, Input<I>
, it doesn't know what type it is; that's what "It is not fully specified" means.
You can fix this by specifying what the generic types are. You do this the same way you did the bindings in your AbstractModule
, e.g instead of binding to ExecutorImpl.class
, hacer:
bind(Executor.class).to(new TypeLiteral<ExecutorImpl<String, Integer>>() {});
contestado el 22 de mayo de 14 a las 14:05
No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas java generics dependency-injection guice or haz tu propia pregunta.
I'm not sure it is possible. For example, what should
injector.getInstance(Executor.class)
inject intoExecutorImpl
, if you have boundOutput
for several generic parameters? This is perfectly valid in Guice, because type literals will be different. - Vladimir Matveev