You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Dagger2为抽象ViewModel类注入构造函数?

问题:将@Inject移到抽象ViewModel类并仅绑定抽象类是否可行?

我有一个抽象ViewModel类:

abstract class AbstractViewModel(
    protected val dataStore: DataStore,
    protected val schedulerProvider: SchedulerProvider
): ViewModel() {
   abstract fun doSomething()
}

多个ViewModel继承自该抽象类:

class InheritedViewModel1 @Inject constructor(
    dataStore: DataStore,
    schedulerProvider: SchedulerProvider
) : AbstractViewModel(dataStore, schedulerProvider) {

    override fun doSomething() {}
}

class InheritedViewModel2 @Inject constructor(
    dataStore: DataStore,
    schedulerProvider: SchedulerProvider
) : AbstractViewModel(dataStore, schedulerProvider) {

    override fun doSomething() {}
}

当前实现可正常运行,但每个子类ViewModel都需在Dagger中单独绑定:

@Binds
@IntoMap
@ViewModelKey(InheritedViewModel1::class)
abstract fun bindInheritedViewModel1(inheritedViewModel1: InheritedViewModel1): ViewModel

@Binds
@IntoMap
@ViewModelKey(InheritedViewModel2::class)
abstract fun bindInheritedViewModel2(inheritedViewModel2: InheritedViewModel2): ViewModel

我想将@Inject注解移到抽象类中,仅绑定抽象类:

abstract class AbstractViewModel @Inject constructor(
    protected val dataStore: DataStore,
    protected val schedulerProvider: SchedulerProvider
): ViewModel() {
   abstract fun doSomething()
}

@Binds
@IntoMap
@ViewModelKey(AbstractViewModel::class)
abstract fun bindAbstractViewModel(abstractViewModel: AbstractViewModel): ViewModel

然后移除所有子类的注入配置,这种方式是否可行?若可行,该如何实例化子类?


答案

这种方式不可行,核心原因有两点:

  1. 抽象类无法被实例化:Dagger依赖注入的本质是创建具体类的实例,抽象类没有可执行的构造实现,Dagger无法生成它的实例对象。
  2. 绑定不匹配实际需求:业务代码中需要的是InheritedViewModel1/InheritedViewModel2这类具体子类的实例,但仅绑定AbstractViewModel的话,Dagger的ViewModel Map中只会存在抽象类的key,无法根据具体子类的类型找到对应的实例。

优化方案:简化子类的绑定代码(减少重复书写)

你不需要移除子类的绑定,而是可以通过复用逻辑来减少重复的注解代码:

方法1:抽象模块类复用绑定逻辑

创建一个通用的抽象Dagger模块,定义ViewModel的绑定模板,子类模块只需继承该抽象类即可:

// 抽象模板模块
abstract class BaseViewModelModule<T : AbstractViewModel> {
    @Binds
    @IntoMap
    @ViewModelKey(T::class)
    abstract fun bindViewModel(viewModel: T): ViewModel
}

// 子类模块(仅需继承,无需重复写注解)
@Module
abstract class InheritedViewModel1Module : BaseViewModelModule<InheritedViewModel1>()

@Module
abstract class InheritedViewModel2Module : BaseViewModelModule<InheritedViewModel2>()

方法2:简化子类的构造注入(可选)

如果将@Inject移到抽象类的构造上,子类可以省略构造参数的声明,直接继承父类的注入逻辑,减少子类的代码量:

abstract class AbstractViewModel @Inject constructor(
    protected val dataStore: DataStore,
    protected val schedulerProvider: SchedulerProvider
): ViewModel() {
   abstract fun doSomething()
}

// 子类无需再声明构造参数,自动继承父类的注入逻辑
class InheritedViewModel1 : AbstractViewModel() {
    override fun doSomething() {}
}

class InheritedViewModel2 : AbstractViewModel() {
    override fun doSomething() {}
}

但注意:即使这样,你仍然需要为每个子类添加Dagger绑定(可以用上面的抽象模块方式简化),因为Dagger需要知道具体子类的存在才能生成实例。


内容的提问来源于stack exchange,提问作者Mehdi Satei

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 08:54:22