30

I am trying to migrate a small project, replacing some factories with Guice (it is my first Guice trial). However, I am stuck when trying to inject generics. I managed to extract a small toy example with two classes and a module:

import com.google.inject.Inject;

public class Console<T> {
  private final StringOutput<T> out;
  @Inject
  public Console(StringOutput<T> out) {
    this.out = out;
  }
  public void print(T t) {
    System.out.println(out.converter(t));
  }
}

public class StringOutput<T> {
  public String converter(T t) {
    return t.toString();
  }
}

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.TypeLiteral;


public class MyModule extends AbstractModule {

  @Override
  protected void configure() {
    bind(StringOutput.class);
    bind(Console.class);
  }

  public static void main(String[] args) {
    Injector injector = Guice.createInjector( new MyModule() );
    StringOutput<Integer> out = injector.getInstance(StringOutput.class);
    System.out.println( out.converter(12) );
    Console<Double> cons = injector.getInstance(Console.class);
    cons.print(123.0);
  }

}

When I run this example, all I got is:

Exception in thread "main" com.google.inject.CreationException: Guice creation errors:

1) playground.StringOutput<T> cannot be used as a key; It is not fully specified.
  at playground.MyModule.configure(MyModule.java:15)

1 error
    at com.google.inject.internal.Errors.throwCreationExceptionIfErrorsExist(Errors.java:354)
    at com.google.inject.InjectorBuilder.initializeStatically(InjectorBuilder.java:152)
    at com.google.inject.InjectorBuilder.build(InjectorBuilder.java:105)
    at com.google.inject.Guice.createInjector(Guice.java:92)

I tried looking for the error message, but without finding any useful hints. Further on the Guice FAQ I stumble upon a question about how to inject generics. I tried to add the following binding in the configure method:

bind(new TypeLiteral<StringOutput<Double>>() {}).toInstance(new StringOutput<Double>());

But without success (same error message).

Can someone explain me the error message and provide me some tips ? Thanks.

paradigmatic
  • 40,153
  • 18
  • 88
  • 147

1 Answers1

29

I think the specific issue you're seeing is probably because of the bind(Console.class) statement. It should use a TypeLiteral as well. Or, you could just bind neither of those and JIT bindings will take care of it for you since both of the types involved here are concrete classes.

Additionally, you should retrieve the Console with:

Console<Double> cons = 
   injector.getInstance(Key.get(new TypeLiteral<Console<Double>>(){}));

Edit: You don't need to bind to an instance just because you're using a TypeLiteral. You can just do:

bind(new TypeLiteral<Console<Double>>(){});

Of course, like I said above you could just skip that in this case and retrieve the Console from the injector using a Key based on the TypeLiteral and the binding would be implicit.

Jeff Axelrod
  • 27,676
  • 31
  • 147
  • 246
ColinD
  • 108,630
  • 30
  • 201
  • 202
  • Thanks. I am not sure how to bind a TypeLitteral to Console because I cannot make an instance of Console without a StringOutput instance. Can you detail ? – paradigmatic Apr 05 '10 at 21:31
  • I just tried. It works only if I define both type litterals AND y use the Key to get the instance. So I am not able to make the binding explicit. – paradigmatic Apr 05 '10 at 22:13
  • I'm not sure what you mean by not being able to make the binding explicit. If you put the bind statement for each in your module, it's explicit. Also, I should note that in general you should only have ONE call to injector.getInstance() in an application, which should retrieve some root application object for you to start. Everything else should be worked out by @Inject annotated constructors and such. So in practice, you shouldn't generally need to use the injector.getInstance(Key.get(new TypeLiteral...)) thing. – ColinD Apr 05 '10 at 22:30
  • 3
    @ColinD Is it also possible not to specify the concrete type T while binding in the Guice Module? – Michael Jan 22 '15 at 16:10
  • i know this is old, but i have what really feels like the same problem here: http://stackoverflow.com/questions/40646780/dependency-injection-using-guice-with-the-dao-pattern and i'm struggling to figure it out. can anyone provide guidance? – Justin Nov 18 '16 at 02:50
  • I had the same issue and according to the suggestion, I bound both classes using `TypeLiterals`. But strangely I get a `java.lang.OutOfMemoryError: Java heap space` error. I think this is the reason, because if I remove the dependency `StringOutput` from `Console`, it works just fine. – rrrocky Apr 05 '18 at 04:40