views:

81

answers:

2

The following code is an example of a factory that produces a Bar<T> given a Foo<T>. The factory doesn't care what T is: for any type T, it can make a Bar<T> from a Foo<T>.

import com.google.inject.*;
import com.google.inject.assistedinject.*;

class Foo<T> {
  public void flip(T x) { System.out.println("flip: " + x); }
}

interface Bar<T> {
  void flipflop(T x);
}

class BarImpl<T> implements Bar<T> {
  Foo<T> foo;

  @Inject
  BarImpl(Foo<T> foo) { this.foo = foo; }

  public void flipflop(T x) { foo.flip(x); System.out.println("flop: " + x); }
}

interface BarFactory {
  <T> Bar<T> create(Foo<T> f);
}

class Module extends AbstractModule {
  public void configure() {
    bind(BarFactory.class)
      .toProvider( 
          FactoryProvider.newFactory( BarFactory.class, BarImpl.class ) 
                   );
  }
}

public class GenericInject {
  public static void main(String[] args) {
    Injector injector = Guice.createInjector(new Module());

    Foo<Integer> foo = new Foo<Integer>();
    Bar<Integer> bar = injector.getInstance(BarFactory.class).create(foo);
    bar.flipflop(0);
  }
}

When I run the code, I get the following errors from Guice:

1) No implementation for BarFactory was bound.
  at Module.configure(GenericInject.java:38)

2) Bar<T> cannot be used as a key; It is not fully specified.

The only reference I can find to generics in the Guice documentation says to use a TypeLiteral. But I don't have a literal type, I have a generic placeholder that isn't relevant to the factory at all. Any tips?

A: 

If you think of guice as a system of wiring similar to spring, then it doesn't really make sense to wire a generic instance. You're wiring specific instances to keys, so that when another instantiated class marks something with @Inject BarFactory you get the specific created instance.

Since your implementation is generic, you haven't provided enough information to inject a specific instance. Although I haven't used factoryprovider, my assumption is that you'll need to bind the Barfactory to a fully parameterized instance, e.g. BarImpl<Concrete> instead of BarImpl )

Incidentally since you're binding BarFactory.class if you want to bind multiple instances you're going to have to inflect them somehow, either by name, something like (haven't checked the syntax, but)

bind(BarFactory.class).annotatedWith(Names.named("name1"))
      .toProvider( 

or by generics, bind(BarFactory<Concrete>).toProvider...
Steve B.
BarFactory isn't generic, so it makes perfect sense to wire it the way I've wired it. A parameterized instance of Bar wouldn't match the contract: create needs to to take a Foo<T> and return a Bar<T> for any T.
Chris Conway
A: 

One option is to just write the BarFactory boilerplate by hand:

class BarImplFactory implements BarFactory {
  public <T> Bar<T> create(Foo<T> f) {
    return new BarImpl(f);
  }
}

The binding becomes

bind(BarFactory.class).to(BarImplFactory.class);
Chris Conway