38

I'm trying to inject things with Google Guice 2.0 and I have the following structure:

FooAction implements Action
BarAction implements Action

I then have an ActionLibrary with the following constructor:

ActionLibrary (List<Action> theActions)

When I request an instance of ActionLibrary from Guice, I would like Guice to identify both of the registered Action classes (FooAction, BarAction) and pass them into the constructor. The motivation here being that when I add a third action BazAction, it would be as simple as registering it in the Module and it would automatically be added to the list in the constructor.

Is this possible?

durron597
  • 31,968
  • 17
  • 99
  • 158
Scruffers
  • 4,984
  • 8
  • 35
  • 41

2 Answers2

50

What you want for this is Multibindings. Specifically, you want to bind a Set<Action> (not a List, but a Set is probably what you really want anyway) like this:

Multibinder<Action> actionBinder = Multibinder.newSetBinder(binder(), Action.class);
actionBinder.addBinding().to(FooAction.class);
actionBinder.addBinding().to(BarAction.class);

Then you can @Inject the Set<Action> anywhere.

Chadwick
  • 12,555
  • 7
  • 49
  • 66
ColinD
  • 108,630
  • 30
  • 201
  • 202
  • 1
    What about if `List` is actually required - and why not even `Collection extends Action>`? – jilt3d May 29 '14 at 14:14
  • 2
    `List` doesn't make sense because the whole idea of `Multibinder` is that it collects bindings from multiple modules... and there isn't a reliable user-defined order for the items. If you really need a `List` with items in a specific order, it only really makes sense to create that list yourself and bind it directly. But the typical use-case for `Multibinder` is binding multiple implementations of an interface, in which case order typically shouldn't matter and you don't want more than one of the same thing. – ColinD May 30 '14 at 14:12
25

Let me show you what I consider an even better way of multibinding things. If you want Actions to be pluggable and let anyone add them, it's often useful to provide a simple Module for someone to use that hides needing to instantiate the Multibinder. Here's an example:

public abstract class ActionModule extends AbstractModule {
  private Multibinder<Action> actionBinder;

  @Override protected void configure() {
    actionBinder = Multibinder.newSetBinder(binder(), Action.class);
    configureActions();
  }

  /**
   * Override this method to call {@link #bindAction}.
   */
  protected abstract void configureActions();

  protected final LinkedBindingBuilder<Action> bindAction() {
    return actionBinder.addBinding();
  }
}

Now why is this better? It allows someone to use an ActionModule from anywhere to add more Actions via the standard binding API. I think it's more readable. Here's an example usage:

public final class MyStandardActionModule extends ActionModule() {
  @Override protected void configureActions() {
    bindAction().to(FooAction.class);
    bindAction().to(BarAction.class);
    // If you need to instantiate an action through a Provider, do this.
    bindAction().toProvider(BazActionProvider.class);
    // You can also scope stuff:
    bindAction().to(MySingletonAction.class).in(Singleton.class);
  }
}

This pattern of using a Module to hide the multibinder is used in Guice code. It's a little work up front, but keeps things clean. You can also do something similar for a MapBinder if you need to. Keep in mind you can instantiate as many ActionModules as you want.

Neeme Praks
  • 8,956
  • 5
  • 47
  • 47
Tom
  • 21,468
  • 6
  • 39
  • 44