Search code examples
androidkotlindagger-2

Inject two objects of the same class with different fields using named in Dagger2


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?


Solution

  • 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?