Cyde Weys
Cyde Weys

Reputation: 254

How do I use inject a field using a Dagger 2 multi-binding?

I'm trying to support third party extensions using map multibindings, but I can't wrap my head around how it's supposed to provide an extensible infrastructure. It seems like it should be really simple, but the Dagger 2 documentation on multibindings doesn't actually provide an example of how to do it. I want other developers to be able to write their own implementations of an interface that I provide, and then have that seamlessly integrate.

Here's some sample code that shows what I'm attempting to do:

// This is the interface that third parties can implement.
public interface FooService {
  public void run();
}

// This is the default implementation of the interface that I provide.
class DefaultFooImpl implements FooService {
  DefaultFooImpl() { ... }
  @Override public void run() { ... }
}

// Third parties would need to add their own modules to provide their
// implementations of FooService on different keys (right?).
@Module class DefaultImplModule {
  @Provides(type = MAP)
  @StringKey("default")
  static FooService provideDefaultImpl() {
    return new DefaultFooImpl();
  }
}

// PROBLEM! This won't work for third-party implementations, since I
// can't include their modules here because I don't know them.
@Component(modules = DefaultImplModule.class)
interface FooServiceComponents {
  Map<String, FooService> fooServices();
}

public class FooDispatcher {
  // PROBLEM! How do I actually inject this map?  Does this work?
  @Inject Map<String, FooService> fooServices;

  void callFooService(String whichService) {
    // whichService is any of the Strings in the combined services map.
    // For the default implementation, you'd pass in "default".
    this.fooServices.get(whichService).run();
  }
}

So what's the missing piece that ties this all together and actually makes it work? Thanks.

Upvotes: 0

Views: 659

Answers (1)

Angad
Angad

Reputation: 2823

Here is the pattern I use - the Module will have to be configured at time of using the Component builder by passing your arguments. Strongly recommend sparing usage and only if needed - things should be as simple as you need them to be, but not any more :-)

@Module
class ConfigurableModule {

    private IDependencyOne one;
    private IDependencyTwo two;
    ...

    ConfigurableModule() {

    }

    ConfigurableModule(IDependencyOne one, IDependencyTwo two, ...) {
        this.one = one;
        this.two = two;
    }


    @Provides IDependencyOne getIDependencyOne(MyIDependencyOneImpl impl) {
        return one == null ? impl : one;
    }

    @Provides IDependencyTwo getIDependencyTwo(MyIDependencyTwoImpl impl) {
        return one == null ? impl : one;
    }
}

Upvotes: 1

Related Questions