Let's say I have a singleton created by Dagger 2 which implements the interfaces Foo
and Bar
. There are places in the code which use Foo
, and other places that use Bar
, but whichever interface is injected I want the same singleton. Is this possible?
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.
@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.
/* 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.
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.
interface FooBar extends Foo, Bar
class FooBarImpl implements FooBar {...}
@SomeScope @Binds FooBar bindsFooBar(impl: FooBarImpl)
@SomeScope @Binds Foo bindsFoo(fooBar: FooBar)
@SomeScope @Binds Bar bindsBar(fooBar: FooBar)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With