Injecting the same object with different interfaces using Dagger
Yes, this is easy to do, but it's also easy to do incorrectly. The trick is that you need to be careful which you make @Singleton
: Foo, Bar, or FooBarImpl.
In Dagger, any particular binding can be marked Singleton, and that's where Dagger interferes to say "let me cache this instance". Interfaces like Foo and Bar will have it marked on the @Binds
or @Provides
method, and concrete classes like FooBarImpl will have it marked on a @Provides
method or on the actual class that has an @Inject
annotation.
Ideal: Mark your implementation @Singleton
@Singleton public class FooBarImpl implements Foo, Bar { /* ... */ }
// in your module:
@Binds Foo bindFoo(FooBarImpl impl);
@Binds Bar bindBar(FooBarImpl impl);
This way, whether a consumer asks for a Foo, Bar, or FooBarImpl, they'll always go to Dagger's internal double-check-locked caching instance ("DoubleCheck"). Foo and Bar can be marked @Singleton
as well, but it's a little wasteful, because the internal FooBarImpl provider will always return the same instance anyway. There's no need to keep a separate copy on the Foo Provider or the Bar Provider.
If FooBarImpl is external or otherwise outside of your control, you can mark @Singleton
on the @Provides
method instead.
Less ideal: Mark one and bind the other to it
/* not Singleton */ public class FooBarImpl implements Foo, Bar { /* ... */ }
// in your module:
@Binds @Singleton Foo bindFoo(FooBarImpl impl);
@Binds Bar bindBar(Foo foo);
Assuming Foo extends Bar, you could bind Bar to Foo. Here, the singleton instance is kept in the Foo provider, injections of FooBarImpl will return new instances, and injections of Bar will consult Foo (and its caching provider) every time.
This sounds strictly worse, but there are valid use-cases: Imagine if FooBarImpl were a CacheImpl of some sort, and you want Foo and Bar to return one instance but @Named("accounts") ListeningCache
and @Named("accounts") Cache
to both return the same distinct instance.
Not what you want: Separate singleton bindings
For completeness:
/* not Singleton */ public class FooBarImpl implements Foo, Bar { /* ... */ }
// in your module:
@Binds @Singleton Foo bindFoo(FooBarImpl impl);
@Binds @Singleton Bar bindBar(FooBarImpl impl);
Here, Foo and Bar will each return a separate Singleton instance, and injections of FooBarImpl will return a new instance each time. This is great for use-cases where you want two or more objects, but be careful not to accidentally implement this solution when you're trying to implement the "less ideal" solution above. They look really similar, but behave very differently.