Suppose I have rectangle class implementing shape interface
class Rectangle @Inject constructor(private val id: String) : Shape {
override fun info() {
Log.d("Rectangle", "I am rectangle!, My id is $id")
}
}
interface Shape {
fun info()
}
One way of providing two objects of the same class with different id using @Named would be inside my module:
@Module
class AppModule {
@Provides
@Named("Id1")
fun provideId1(): Shape {
return Rectangle("Id1")
}
@Provides
@Named("Id2")
fun provideId2(): Shape {
return Rectangle("Id2")
}
}
With that I can inject two rectangle objects in my mainactivity
@Inject
@field:Named("Id1")
lateinit var rect1: Shape
@Inject
@field:Named("Id2")
lateinit var rect2: Shape
However I would like my Module to look little bit different. Inside it I want to provide shape and using @named constructor parameters for objects. What I have is:
@Module
class AppModule {
@Provides
@Named("Id1")
fun provideId1(@Named("id1") id: String): Shape {
return Rectangle(id)
}
@Provides
@Named("Id2")
fun provideId2(@Named("id2") id: String): Shape {
return Rectangle(id)
}
@Provides
@Named("id1")
fun provideId1(): String {
return "ABC"
}
@Provides
@Named("id2")
fun provideId2(): String {
return "XYZ"
}
}
How can I achieve injection of two objects with different constructor parameters using such approach?
CodePudding user response:
This is the key part you're missing:
@Module
class AppModule {
@Provides
@Named("ShapeId1")
fun provideId1(@Named("id1") id: String): Shape {
return Rectangle(id)
}
@Provides
@Named("ShapeId2")
fun provideId2(@Named("id2") id: String): Shape {
return Rectangle(id)
}
}
the part that automates putting provided named strings into actual rectangle constructors.
however, i'd go with something simpler/cleaner like this:
RectangleNamed1Module.kt:
@Module(includes = [RectangleNamed1ProvidingModule::class])
internal interface RectangleNamed1Module {
@Binds
@Named("ShapeID1")
fun bindRectangle(rectangle: Rectangle): Shape
}
@Module
internal object RectangleNamed1ProvidingModule {
@Provides
fun provideRectangle(
@Named("id1") stringId: String
): Rectangle = Rectangle(stringId)
}
then you can just... copypaste this module as many times as you need and slightly change the named dependencies/parameters and thats about it.
You can use it as RectangleNamed1Module and RectangleNamed2Module, then just make those two modules a dependancy of your AppModule. and wherever you're trying to inject two shapes/rectangles named "Shape1" and "Shape2" should work, right?
CodePudding user response:
As per my comment,
Aha, i got it. I think. They can't be in the same module. The reason is: while the module is being processed, it will provide named strings, but they will not be in the graph to meet the criteria for providing the rectangle-providing methods with those named parameters. You will need to move string-providing methods to a different module, and make your AppModule use it as a dependency. Then it should work. However, this is just a best-effort guess
try something like this instead:
@Module(include = StringProvidingModule::class)
class AppModule {
@Provides
@Named("Id1")
fun provideId1(@Named("id1") id: String): Shape {
return Rectangle(id)
}
@Provides
@Named("Id2")
fun provideId2(@Named("id2") id: String): Shape {
return Rectangle(id)
}
}
@Module
class StringProvidingModule {
@Provides
@Named("id1")
fun provideId1(): String {
return "ABC"
}
@Provides
@Named("id2")
fun provideId2(): String {
return "XYZ"
}
}