如何使用 Hilt 注入 Map

How to inject a Map using Hilt

我有这样的设置:

interface Animal {

}

class Dog @Inject constructor() : Animal

class AnimalProxy @Inject constructor(
    val animalFactory: AnimalFactory,
    val animalMap: Map<AnimalType, Animal>
) : Animal

enum class AnimalType {
    Pet,
    Wild
}
class AnimalFactory @Inject constructor ()

这就是我在模块中绑定这些对象的方式

@Module
@InstallIn(SingletonComponent::class)
class AnimalModule {

    @MapKey
    annotation class AnimalTypeKey(val value: AnimalType)

    @Named(DOG)
    @Provides
    fun provideDog(
    ): Animal {
        return Dog()
    }

    @Provides
    @Singleton
    @IntoMap
    @AnimalTypeKey(AnimalType.Pet)
    @Named(PROXY)
    fun provideAnimalProxy(
        animalProxy: AnimalProxy
    ) : Animal = animalProxy

    companion object {
        const val DOG = "dog"
        const val PROXY = "proxy"
    }
}

但不知何故有些事情不太对劲,我无法弄清楚发生了什么。我收到一个错误 cannot be provided without an @provides-annotated method。我在创建 provideAnimalProxy 时知道出了点问题,但无法弄清楚。我的其他工作选项是:

@InstallIn(SingletonComponent::class)
@Module
class AnimalsModule {

    @Singleton
    @Provides
    fun provideProxy(
        
    ): Animal {
        return AnimalProxy(
            AnimalFactory(),
            mapOf(
                AnimalType.Pet to Dog(),
            ),
        )
    }
}

但是对于 AnimalFactory 我已经有了一个 inject 构造函数。

@Named(DOG)
@Provides
fun provideDog(
): Animal {        // Provides @Named(DOG) Animal
    return Dog()   //   which will return a Dog()
}

@Provides
@Singleton
@IntoMap
@AnimalTypeKey(AnimalType.Pet)  // Binds the key Pet
@Named(PROXY)                   // into @Named(PROXY) Map<AnimalType, Animal>
fun provideAnimalProxy(
    animalProxy: AnimalProxy
) : Animal = animalProxy        //   which will return an AnimalProxy()

听起来您想要的是使用其 @Inject 构造函数在地图外部绑定 AnimalProxy。看起来像这样:

@Provides
@IntoMap                       // @IntoMap here means Map<key, Animal>
                               //   because the function returns Animal
@AnimalTypeKey(AnimalType.Pet) // @AnimalTypeKey(Pet) means the key is Pet
                               // Nothing is @Named, so the map isn't named
fun provideDog(
): Animal {                    // Binds Pet into Map<AnimalType, Animal>
    return Dog()               //   which will return a Dog()
}

或者,由于 Dog 有一个 @Inject 带注释的构造函数,您可以简化为:

@Provides
@IntoMap
@AnimalTypeKey(AnimalType.Pet)
fun provideDog(dog: Dog): Animal = dog

或与,抽象class或接口:

@Binds
@IntoMap
@AnimalTypeKey(AnimalType.Pet)
abstract fun bindDog(dog: Dog): Animal

对于 AnimalProxy,您不应该对要注入的对象使用 @Provides 方法;如果你想要 @Singleton ,你可以注释 AnimalProxy class 本身。您可以完全删除模块的那部分。

但是,您可能希望 AnimalProxy 出现在 Map<AnimalType, Animal> 本身中,这听起来像是一个循环引用:要创建地图,您需要创建每个动物,包括代理,这需要创建您当前正在尝试创建的相同地图。如果是这种情况,您确实有一个解决方法:您可以 automatically inject a Map<AnimalType, Provider<Animal>> the same way you can inject a Provider<Dog> rather than just a Dog 而不是注入 Map<AnimalType, Animal>。这样你就不会在构造函数中表达对真正的 AnimalProxy(或其 Map)的需求,这样 Dagger 就不会抱怨了。