Good questions that help to shed light on how Guice itself works, and the distinctions between Guice and Gin. Gin is not quite the same as Guice - the configure()
method runs when generating your JavaScript, so that the compiler only bakes in the right set of types - otherwise your app could potentially contain the whole JRE! This is slightly cheating for Gin to do this, and once you understand this, GWT DI makes a little more sense.
The basic idea is that the configure()
method is only supposed to deal with wiring - not creating instances. This provides the answer to 1), and part of the answer to 2). Actually writing code that will be used when the app is running (Provider
objects, @Provides
methods, and of course anything annotated with @Inject
) needs to be the other way around - it will be compiled only into JS. This means that while you can define methods like configureBuzzes
in 3), you need to be careful only to ever call these from inside the configure()
method - and never call configure()
from regular app code.
The answers for 2), 3), and 4) are mostly to do with how Guice itself generally works. The solution I provide for 1) also works in normal Guice, and I would go so far as to suggest this approach all the time - I find it tend to make more readable code if you don't mix the wiring and the actual object building.
Don't create the instances in your
configure()
method, just do the bindings. You can set the binding to be a For examplebind(EventBus.class).to(SimpleEventBus.class).in(Singleton.class);
creates the instance, and scopes it to be a singleton - the default constructor will be used by default.
If you want to use a non-default constructor, there are several options. You could annotate the particular constructor with
@Inject
, and provide some annotation for each value (more on that in a moment), or you could build a provider or@Provides
method to create the instance. Again, you may want@Singleton
to have this make sense, but that'll depend on your use case (this will be another method in your GinModule):@Provides //@Singleton //optional, depends on your use case public Fizz provideFirstFizz() { return new Fizz(true, "oh yeah", null); }
Next, how do you provide two different kinds of the same thing? How do you do this in Guice? And how would you expect your code that gets a
Fizz
injected to get the right one? It turns out these probably all have the same answer - you need to find a way to indicate which instance you want. They are all the same type, so that isn't enough, but we can provide other hints, like an annotation on the injected field. Say our code that will needf1
andf2
looks like this@Inject @Red// Make up your own annotation, or use some existing ones private Fizz f1; @Inject @Blue private f2;
Now we have a way to tell the difference, and we need to bind them using those same annotations. Since we're still assuming no
@Inject
on theFizz
constructor, we can't just do abind()
call, so instead we'll just add@Blue
to the provides method:@Provides @Blue //@Singleton //optional, depends on your use case public Fizz provideFirstFizz() { return new Fizz(true, "oh yeah", null); }
We can read this as "This method
Provides
Blue
Fizz instances." For@Red
, since we have the default ctor, we can usebind()
:bind(Fizz.class).annotatedWith(Red.class);//... no need to specify type //in this case, but might want //singleton
See https://code.google.com/p/google-guice/wiki/BindingAnnotations for more details on this.
Again, we can use
@Provides
for this, or create and bind aProvider<T>
type. As we've already done several provider methods, lets try aProvider<List<Buzz>>
:public class BuzzListProvider implements Provider<List<Buzz>> { public List<Buzz> get() { List<Buzz> buzzes = new ArrayList<Buzz>(); // Configure them... This might call on a @Inject defined // within this BuzzListProvider, on the ctor or a field, or // just some code in this method. return buzzes; } }
Then, bind the Provider to that List:
// cant say List<Buzz>.class, use TypeLiteral instead bind(new TypeLiteral<List<Buzz>>(){}) .toProvider(BuzzListProvider.class); // .in(Singleton.class); if the list needs to be only created once
You're exactly right in your summary - this is exactly the same as 2. I usually make a
@DefaultPlace
annotation (or just plain@Default
so I can reuse it all over) to deal with this kind of case.