Inyectar una implementación de un subtipo de interfaz genérica usando Guice

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)

preguntado el 22 de mayo de 14 a las 13:05

I'm not sure it is possible. For example, what should injector.getInstance(Executor.class) inject into ExecutorImpl, if you have bound Output for several generic parameters? This is perfectly valid in Guice, because type literals will be different. -

1 Respuestas

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 or haz tu propia pregunta.