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.