如何为Dagger多绑定(Multibindings)指定顺序?
我完全懂你的困扰——用Dagger的@IntoSet做多绑定时,默认是没法直接控制元素顺序的,就像你例子里想先喂山羊再喂牛,直接用Set确实做不到,而且用IntoMap加数字键又感觉不够优雅,毕竟Spring里有现成的排序注解可以用。
先回顾下你的场景代码,方便大家理解问题:
首先是需要注入Set的Farm类:
class Farm @Inject constructor( private val animals: Set<@JvmSuppressWildcards Animal>, ){ init { animals.forEach { feed(it) } } }
然后是分模块的绑定:
//module A @Binds @IntoSet abstract fun goat(goat: Goat): Animal //module B @Provides @IntoSet fun cow(): Animal = Cow(...)
接下来给你几个更优雅的解决方案,不用生硬地用int键的Map:
方案1:注入排序后的List(最灵活)
既然Set本身是无序的,我们可以让Dagger注入一个预先排序好的List,而不是直接注入Set。
首先定义一个自定义限定符来标识这个有序集合:
@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class OrderedAnimals
然后提供一个被排序后的List实例:
@Provides @OrderedAnimals fun provideOrderedAnimals(animals: Set<@JvmSuppressWildcards Animal>): List<Animal> { // 在这里自定义排序逻辑,比如按类型优先级排序 return animals.sortedBy { when (it) { is Goat -> 0 // 山羊优先级最高,排前面 is Cow -> 1 // 牛次之 else -> 2 // 其他动物排最后 } } }
最后修改Farm类,注入这个有序List:
class Farm @Inject constructor( @OrderedAnimals private val animals: List<Animal>, ){ init { animals.forEach { feed(it) } } }
这个方案的好处是完全解耦,不需要修改Animal的实现类,排序逻辑集中在Provider里,想调整顺序直接改这里就行。
方案2:带优先级注解的Map绑定(比纯int键更清晰)
如果你还是想用类似Spring排序注解的思路,可以给每个绑定加一个带优先级的限定符,然后用Map收集后排序。
首先定义两个注解:一个用于标记优先级,一个用于标识这个排序用的Map:
// 标记每个Animal的优先级 @Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class AnimalPriority(val value: Int) // 标识用于排序的Map @Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class PriorityKeyedAnimals
然后修改绑定代码,用@IntoMap结合优先级注解:
//module A - 山羊优先级设为0 @Binds @IntoMap @AnimalPriority(0) @PriorityKeyedAnimals abstract fun goat(goat: Goat): Animal //module B - 牛优先级设为1 @Provides @IntoMap @AnimalPriority(1) @PriorityKeyedAnimals fun cow(): Animal = Cow(...)
接下来提供一个排序后的List:
@Provides fun provideOrderedAnimals( @PriorityKeyedAnimals animalMap: Map<Int, @JvmSuppressWildcards Animal> ): List<Animal> { // 按优先级键排序后取值 return animalMap.toSortedMap().values.toList() }
最后Farm注入这个List即可。这个方案更贴近Spring排序注解的思路,优先级明确标注在绑定上,可读性更高。
方案3:让Animal实现Comparable接口(适合简单场景)
如果所有Animal的实现类都能统一排序逻辑,可以让它们实现Comparable接口,然后注入SortedSet:
// 山羊实现Comparable,排在牛前面 class Goat : Animal, Comparable<Animal> { override fun compareTo(other: Animal): Int { return if (other is Cow) -1 else 0 } } // 牛实现Comparable,排在山羊后面 class Cow : Animal, Comparable<Animal> { override fun compareTo(other: Animal): Int { return if (other is Goat) 1 else 0 } }
然后修改Farm的注入类型:
class Farm @Inject constructor( private val animals: SortedSet<@JvmSuppressWildcards Animal>, ){ init { animals.forEach { feed(it) } } }
这个方案的优点是简洁,但缺点也很明显:排序逻辑耦合在Animal实现类里,如果后续要调整顺序或者新增Animal类型,都要修改实现类,灵活性较差,适合排序逻辑固定的简单场景。
备注:内容来源于stack exchange,提问作者Vlad

