Is it possible to pass type parameter information through to a dagger subcomponent?

192 views Asked by At

I'd like to be able to expose type information to a subcomponent inside a generic abstract class. We have an abstract class with a type parameter that is set in a subclass (the latter of which is bound in our graph), and try to pass this type information to a subcomponent inside this class. The way attempted tries to attach a type parameter to the subcomponent.

Here is a more fleshed-out example of what I'd like to achieve:

interface Runner<T> {
    fun run(t: T)
}

data class Data<T>(
    val options: T
)

abstract class AbstractRunner<Options> : Runner<Data<Options>> {

    @Inject
    lateinit var mySubcomponentBuilder: MySubcomponent.Builder<Options>

    // Constructing this relies on dynamicData which must be generated at runtime
    // (hence the subcomponent)
    // @Inject
    // lateinit var subRunner: Runner<Options>

    override fun run(data: Data<Options>) {

        // Some dynamically generated data - we can't use assisted injection as this is injected into other classes in
        // the subgraph.
        val dynamicData = Random.nextInt()

        mySubcomponentBuilder
            .dynamicData(dynamicData)
            .build()
            .subcomponentRunner()
            .run(data.options)
    }
}

// We have other objects with associated Runner implementations;
// we list one for brevity.
object Foo
class MyRunner @Inject constructor() : AbstractRunner<Foo>()
class SubRunner @Inject constructor(s: String) : Runner<Foo> {
    override fun run(t: Foo) {}
}

@Component(modules=[MyModule::class])
interface MyComponent {
    fun runner(): Runner<Data<Foo>>
}

@Module(subcomponents=[MySubcomponent::class])
interface MyModule {
    @Binds
    fun bindMyRunner(runner: MyRunner): Runner<Data<Foo>>
}

// This does not work due to generics being banned on (Sub)Components.
@Subcomponent(modules=[SubModule::class])
interface MySubcomponent<T> {
    // We can't parameterise the method; generics are banned on methods in (Sub)Components.
    fun subcomponentRunner(): Runner<T>

    @Subcomponent.Builder
    interface Builder<U> {
        @BindsInstance
        fun dynamicData(i: Int): Builder<U>
        fun build(): MySubcomponent<U>
    }
}

@Module
object SubModule {
    @Provides
    fun provideString(i: Int) = i.toString()
    @Provides
    fun provideSubRunner(s: String): Runner<Foo> = SubRunner(s)
}

Of course, this does not take:

error: @Subcomponent.Builder types must not have any generic types

Is it possible to wire through this information some other way? If the variable bound in our subcomponent were not needed, we could just bind our subRunner: Runner<Options> in our main graph without issue, as commented.

0

There are 0 answers