Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inject Generic Implementation using Guice

I would like to be able to inject a generic implementation of a generic interface using Guice.

public interface Repository<T> {   void save(T item);   T get(int id); }  public MyRepository<T> implements Repository<T> {   @Override   public void save(T item) {     // do saving     return item;   }   @Override   public T get(int id) {     // get item and return   } } 

In C# using Castle.Windsor, I'd be able to do:

Component.For(typeof(Repository<>)).ImplementedBy(typeof(MyRepository<>)) 

but I don't think the equivalent exists in Guice. I know I can use TypeLiteral in Guice to register individual implementations, but is there any way to register them all at once like in Windsor?

Edit:

Here's an example of usage:

Injector injector = Guice.createInjector(new MyModule()); Repository<Class1> repo1 = injector.getInstance(new Key<Repository<Class1>>() {}); Repository<Class2> repo2 = injector.getInstance(new Key<Repository<Class2>>() {}); 

Although the more likely usage would be injection into another class:

public class ClassThatUsesRepository {   private Repository<Class1> repository;    @Inject   public ClassThatUsesRepository(Repository<Class1> repository) {     this.repository = repository;   } } 
like image 406
Sean Carpenter Avatar asked Nov 21 '10 16:11

Sean Carpenter


People also ask

How do you inject a Guice?

To use it, annotate the constructor with the @Inject annotation. This constructor should accept class dependencies as parameters. Most constructors will then assign the parameters to final fields. If your class has no @Inject -annotated constructor, Guice will use a public, no-arguments constructor if it exists.

How does @inject work Guice?

Using GuiceIn each of your constructors that need to have something injected in them, you just add an @Inject annotation and that tells Guice to do it's thing. Guice figures out how to give you an Emailer based on the type. If it's a simple object, it'll instantiate it and pass it in.

What is Guice dependency injection?

Guice is an open source, Java-based dependency injection framework. It is quiet lightweight and is actively developed/managed by Google. This tutorial covers most of the topics required for a basic understanding of Google Guice and to get a feel of how it works.

Does Google use Guice?

Use of Google Guice for implementing dependency injection in application is very easy and it does it beautifully. It's used in Google APIs so we can assume that it's highly tested and reliable code.


2 Answers

In order to use generics with Guice you need to use the TypeLiteral class to bind the generic variants. This is an example of how you're Guice injector configuration could look like:

package your-application.com;  import com.google.inject.AbstractModule; import com.google.inject.TypeLiteral;  public class MyModule extends AbstractModule {   @Override   protected void configure() {     bind(new TypeLiteral<Repository<Class1>>(){})       .to(new TypeLiteral<MyRepository<Class1>>(){});   } } 

(Repository is the generic interface, MyRepository is the generic implementation, Class1 is the specific class used in the generics).

like image 183
Kdeveloper Avatar answered Oct 16 '22 08:10

Kdeveloper


Generics not being retained at run-time sure made it harder to grasp the concept at first. Anyways, there are reasons new ArrayList<String>().getClass() returns Class<?> and not Class<String> and although its safe to cast it to Class<? extends String> you should remember that generics are there just for compile-time type checks (sort of like implicit validation, if you will).

So if you want to use Guice to inject MyRepository (with any type) implementation whenever you need a new instance of Repository (with any type) then you don't have to think about generics at all, but you're on your own to ensure type safety (that's why you get those pesky "unchecked" warning).

Here is an example of code working just fine:

public class GuiceTest extends AbstractModule {      @Inject     List collection;      public static void main(String[] args) {         GuiceTest app = new GuiceTest();         app.test();     }      public void test(){         Injector injector = Guice.createInjector(new GuiceTest());         injector.injectMembers(this);          List<String> strCollection = collection;         strCollection.add("I'm a String");         System.out.println(collection.get(0));          List<Integer> intCollection = collection;         intCollection.add(new Integer(33));         System.out.println(collection.get(1));     }      @Override     protected void configure() {         bind(List.class).to(LinkedList.class);     } } 

This prints:

I'm a String 33 

But that list is implemented by a LinkedList. Although in this example, if you tried to asign an int something that is String you would get an exception.

int i = collection.get(0) 

But if you want to get an injectable object already type-casted and dandy you can ask for List<String> instead of just List, but then Guice will treat that Type variable as part of the binding key (similar to a qualifier such as @Named). What this means is that if you want injection specifically List<String> to be of ArrayList<String> implementation and List<Integer> to be of LinkedList<Integer>, Guice lets you do that (not tested, educated guess).

But there's a catch:

    @Override     protected void configure() {         bind(List<String>.class).to(LinkedList<String>.class); <-- *Not Happening*     } 

As you might notice class literals aren't generic. That's where you use Guice's TypeLiterals.

    @Override     protected void configure() {         bind(new TypeLiteral<List<String>>(){}).to(new TypeLiteral<LinkedList<String>>(){});     } 

TypeLiterals retain the generic type variable as part of meta-information to map to desired implementation. Hope this helps.

like image 43
SGal Avatar answered Oct 16 '22 08:10

SGal