google / dagger

A fast dependency injector for Android and Java.
https://dagger.dev
Apache License 2.0
17.45k stars 2.02k forks source link

Multibindings limitations (Lazy values, Multiple keys for a value) #3908

Open maksym-moroz opened 1 year ago

maksym-moroz commented 1 year ago

I am trying to utilize Dagger multibinding capabilities to abstract away some menial work. I have a multi module Gradle project and I am using @IntoMap multibindings in feature modules to then use the whole map in the app module without passing and wiring everything directly.

Right now I am facing two issues

  1. I would like to postpone the costly initialization of my components to a later (less critical) point in time, and since those components' initializations are costly I would like for them to stick around so on the next invocation I get the same ones. Right now, Dagger doesn't provide support for Map<Key, Lazy<Value>> and I wasn't able to find anything concrete on this topic. As a temporary solution, I am able to achieve the desired effect by asking for Map<Key, <Provider<Value>> while scoping the provisions themselves with a Singleton scope but I would like to the see the direct support for Lazy multibindings or the reason why there is no out-of-the-box support for ones.
  2. In my business logic the same component can have several aliases and thus might require multiple keys. As of now I wasn't able to make Dagger do this. It's possible to provide them by hand, essentially just writing the same provision with different keys so it should be possible for Dagger to support this use case out-of-the-box as well.

This is what I imagine it could look like

  @Provides
  @IntoMap
  @RouteKey("one")
  @RouteKey("two")
  @RouteKey("three")

Where @RouteKey is a MapKey annotated with @Repeatable. I think allowing default @StringKey to be repeatable would be ideal while teaching Dagger to support several keys/aliases for custom map keys via repeatable annotation would be a good start.

bcorso commented 1 year ago

For #1 that feature request is filed here (https://github.com/google/dagger/issues/1630).

For #2 there's no existing feature request, but there's a few ways you can work around it by adding a bit of extra setup. For example, one option is to bind the same implementation to multiple keys:

@Module
interface MyModule {
  @Binds
  @IntoMap
  @RouteKey("one")
  Value bindImplOne(ValueImpl impl);

  @Binds
  @IntoMap
  @RouteKey("two")
  Value bindImplTwo(ValueImpl impl);

  @Binds
  @IntoMap
  @RouteKey("three")
  Value bindImplThree(ValueImpl impl);

  // Possibly scope this if you need the map to return the same instance for each key
  static class ValueImpl extends Value {
    @Inject
    ValueImpl(...) {...}
  }
}
maksym-moroz commented 1 year ago

Thanks for a quick response. This is more or the less the way I implemented it so far, just wanted to create this issue