Hi! If I have ```interface A interface B class ...
# dagger
d
Hi! If I have
Copy code
interface A
interface B

class AB : A, B
And I want to have a single intance of
AB
provided as
A
and
B
separately:
Copy code
@Provides fun providesA(): A
@Provides fun providesB(): B
How is this normally done in dagger? Do I do it like this:
Copy code
@Provides fun providesAB(): AB
@Provides fun providesA(ab: AB): A = ab
@Provides fun providesB(ab: AB): B = ab
Feels like there could be a way to avoid manually writing such basic methods. Also don't like the fact that I have to provide
AB
too, although I'd prefer it to be an implemenation detail
n
You should update AB to have an
@Inject
annotation on its constructor and use
@Binds
annotation instead of provides. It should look something like this (sorry on mobile if formatting is weird)
Copy code
class AB @Inject constructor(): A,B
In your module
Copy code
@Binds fun bindA(impl: AB) A 
@Binds fun bindB(impl: AB): B
You might be able to remove the Module altogether if you used Anvil although I'm not sure the Anvil
@ContributesBinding
annotation can bind an implementation to multiple interfaces
Just noticed the single instance part of your original question, you'll have to annotate AB with whatever your component scope is so it's a singleton. Assuming your AppComponent is using the
@Singleton
annotation then AB would look like this
Copy code
@Singleton class AB @Inject constructor(): A,B
d
I am using anvil 🙂
but I cannot add
@ContributesBinding
, because this class is provided using this pattern in several distinct anvil scopes, in different modules
n
I'm pretty sure Anvil annotations are repeatable so you could have something like this if you had access to all your scopes in the module AB is defined in
Copy code
@ContributesBinding(AppScope::class, boundType = A::class)
@ContributesBinding(AppScope::class, boundType = B::class)
@ContributesBinding(OtherScope::class, boundType = A::class)
@ContributesBinding(OtherScope::class, boundType = B::class)
@Singleton
class AB @Inject constructor(): A, B
But I'm not sure your module structure so maybe this isn't possible 🤷‍♀️
d
yep, that's the problem in my case. The AB class is a "core" one and it is in the module which doesn't even have dagger deps and this module is used in "modules". So having it the other way around and make AB-containing module "know" about all features is not possible in my case. I thought maybe there's something which would allow me to say "alias this to this" in feature module
m
You can actually hide AB if you want with some dagger voodoo:
Copy code
@Retention(BINARY)
@Qualifier
private annotation class InternalApi

@Module
object MyModule {
    @Provides
    @InternalApi
    fun providesAB(): AB = ....

    @Provides fun providesA(@InternalApi ab: AB): A = ab
   
    @Provides fun providesB(@InternalApi ab: AB): B = ab
}
It's sort of a party trick. You're creating a qualifier for the actual instance of AB. However, no one will be able to inject that instance of AB because they won't be able to see the qualifier because it's private to this particular file.
This is also really helpful when you use anvil, because it sort of puts a clear delineation on what is public and what is not. So when you have test modules that replace other modules, it's very clear what you actually need to expose. A great example would be something like http interceptors, which you don't want exposed to the rest of the application. They are generally only needed by OkHttpClient instances, but sometimes they may be shared (like a logging interceptor) by more than one OkHttpClient instance (which can be a thing when you have different requirements based on different endpoints). So not only are you hiding these details, it's abundently clear when you make your test module that replaces the client (with say a mock interceptor) only needs to provide OkHttpClient instances, and not interceptors.
d
Wow this is a neat (and hacky) trick! I often think that dagger makes it hard to have "internal" deps, so everything is exposed to any consuming modules...
m
I agree. I wish dagger had a way to limit what objects in the graph were available to the injection points vs things that are internal to the graph. You can use tricks like this (or a few others i've learned along the way using things like dependent components), but at best they are hacks.