首页 > 解决方案 > 如果找不到@Named,则使用匕首的默认实现

问题描述

我面临的问题是我有一个基类和多个子类。为了解决特定的子类,我在 Dagger 2 中使用 @Named 注释。我想要实现的是,如果我使用 @Named("Child3") 注入并且没有使用 @Named("Child3") 提供,那么我应该默认获取 Base 类的实例。

public class BaseClass {

    public void haveFun(){
        System.out.print("Having fun base");
    }

}

public class Child1 extends BaseClass {
    @Override
    public void haveFun() {
        System.out.print("Having fun Child1");
    }
}

public class Child2 extends BaseClass {
    @Override
    public void haveFun() {
        System.out.print("Having fun Child2");
    }
}

现在在模块中,我提供这样的对象:

@Provides
@Named("Child1")
static BaseClass provideChild1(){
    return new Child1();
}

@Provides
@Named("Child2")
static BaseClass provideChild2(){
    return new Child2();
}

@Provides
static BaseClass provideBaseClass(){
    return new BaseClass();
}

现在在我的活动中,我这样注入:

public class ReceiptActivity extends AppCompatActivity {

    @Inject @Named("Child1") BaseClass child1;
    @Inject @Named("Child2") BaseClass child2;
    @Inject @Named("Child3") BaseClass child3;

    // ...

}

由于@Named("Child3")未提供编译时错误,但我想要的是如果@Named("Child3")不存在我应该获得一个BaseClass实例。

我怎样才能做到这一点?

标签: javaandroiddagger-2

解决方案


不幸的是,限定绑定(使用限定符注释的绑定,如@Named)并没有真正的回退或默认值。每个绑定都是不同的,并且不同的绑定不被认为是相关的。这使得很难像您要求的那样编写回退逻辑。

这也是有道理的:尽管共享一种基本类型@Named("Porsche") Engine@Named("Lawnmower") Engine但永远不会真正相互替代。它们是完全不同的依赖关系,如果你缺少 a @Named("Porsche") Engine,Dagger 遵循它应该在编译时失败而不是四处寻找不匹配或不合格的引擎的策略。

相反,您可以绑定 Map 或使用Multibindings来指示您正在寻找的可替代性或灵活性。与其注入绑定本身,不如注入一个映射,或者注入一个封装映射并为您提取正确绑定的工厂。

// This uses Multibindings, but you could manually create a Map instead.

@Binds @IntoMap @StringKey("Child1")
abstract BaseClass provideChild1(Child1 child1);

@Binds @IntoMap @StringKey("Child2")
abstract BaseClass provideChild2(Child2 child2);

// Then in your consumer...

@Inject Map<String, BaseClass> mapOfBaseClasses;
@Inject BaseClass baseClass;

// Or make an injectable Factory:

public class YourClassFactory {
  private final Map<String, Provider<BaseClass>> baseClassMap;
  private final Provider<BaseClass> baseClassProvider;

  @Inject public YourClassFactory(/* ... */) { /* set fields */ }

  public BaseClass get(String key) { /* write fallback logic here */ }
}

如果你有一个特定的绑定可能存在也可能不存在,你也可以使用@BindsOptionalOf来指示在编译时允许该绑定丢失,然后你可以在运行时检测它。

@BindsOptionalOf @Named("Child3")
abstract BaseClass provideOptionalOfChild3();

// Then in your consumer:

private final BaseClass baseClass;

@Inject public YourConsumer(
    @Named("Child3") Optional<BaseClass> optionalChild3,
    Provider<BaseClass> defaultBaseClass) {
  baseClass =
      optionalChild3.isPresent()
      ? optionalChild3.get()
      : defaultBaseClass.get();
}

推荐阅读