这里的问题是:您调用的是哪个inject 方法,Java 可以在编译时确定吗?
如"A note about covariance" 中所述,Dagger 将为您定义的任何成员注入方法生成代码,但仅您传入的静态类型。
@Component public interface YourComponent {
void injectBase(BaseActivity baseActivity);
void injectA(ActivityA activityA);
void injectB(ActivityB activityB);
void injectC(ActivityC activityC);
}
当调用injectA 并传递一个ActivityA 实例时,您将获得ActivityA 中定义的字段的注入,包括BaseActivity 中的字段。与 ActivityB 和 ActivityC 相同。但是,如果你调用injectBase,Dagger 只会注入属于 BaseActivity 的字段,即使你传入的对象恰好是 ActivityA、ActivityB 或 ActivityC。 Dagger 在编译时生成代码,所以如果你调用injectBase,注入只会发生在 BaseActivity 上的字段——因为那是为 BaseActivity 的成员注入器生成的代码,而这些是 Dagger 知道如何注入的唯一字段一个 BaseActivity 参数。
自然因为BaseActivity只知道this是BaseActivity的一个子类型,所以它只能调用injectBase而不是任何具体的子类型。重要的是,即使所有名称 injectBase、injectA 等都相同(如 inject),这仍然适用。 JVM 将选择它在编译时可以确定的最窄的重载,这将是inject(BaseActivity),它将注入 BaseActivity 的字段而不是子类型中的任何内容。如果您要对它们进行唯一命名,您会看到您调用的是哪一个,以及它为什么不注入子类型字段。
泛型在这里无济于事:您正在寻找您的组件来生成和调用 ActivityA、ActivityB 和 ActivityC 的成员注入器。泛型将被删除,但此外组件不能采用 BaseActivity 的任意子类:Dagger 无法在编译时为它可能只在运行时遇到的类型生成代码。您确实需要在编译时在 Dagger 中准备这些类型。
解决此问题的一种方法是允许子类型自行注入。子类型知道 this 是 ActivityA(等等),即使代码看起来逐字符相同,Java 也可以识别正确的类型并正确编译。
// in BaseActivity
protected abstract void injectDependencies();
// in ActivityA
@Override protected void injectDependencies() { component.injectA(this); }
但是,最近发布的另一个选项是使用dagger.android,它使用多重绑定和(有效地)Map<Class, MembersInjector> 以动态注入您想要的特定类型。这也适用于超类,直到您可以让您的 Activity 扩展 DaggerActivity 并且一切都将按照您想要的方式工作。 (请参阅 dagger.android.support 包以获取等效的 AppCompatActivity DaggerAppCompatActivity。)