我正在处理像这样的对象层次结构:
class Foo extends View
{
@Inject BaseDependency dep;
Foo() // Default android view constructors
{
injectDependencies();
}
protected void injectDependencies()
{
getApp().getFooComponent().inject(this);
}
}
class ExtraBaseDependency extends BaseDependency
{
@Inject
ExtraBaseDependency() {}
}
class Bar extends Foo
{
@Inject ExtraBaseDependency dep;
@Override
protected void injectDependencies()
{
getApp().getBarComponent().inject(this);
}
}
我试图解决的问题是,我想在我的更具体的'Bar'子类中使用更多方法的'ExtraBaseDependency',而且我也希望为Foo提供与我收集数据相同的对象从整个层次结构中使用Bar。
我认为,通过重写injectDependencies()而不是调用super,我可以为我的子类及其父提供他们需要的依赖关系,但是只有其中一个要共享,但Bar会得到对象的更具体的接口所以它可以完成它的工作。
我看到的行为是,当它编译并运行时,我看到ExtraBaseDependency被构造了两次。
我的FooComponent和BarComponents具有不同的范围和不同的模块,它们是分开的。我不确定为什么我没有在基类中获得与子类相同的实例。
答案 0 :(得分:2)
你的两个领域都将继续存在; fields are not polymorphic in Java。 Foo无法访问Bar.dep
,但Bar可以通过Foo.dep
访问super.dep
。
Dagger将自动注入您注入的任何类型的超类型字段和方法,
所以,如果你打电话给inject(Bar)
,Dagger会注入Foo的字段以及Bar的字段。 (每a note about covariance,你必须小心不要调用inject(Foo)
,因为即使你在运行时传入一个Bar实例,它也只会注入Foo的字段。See more here, but injecting without explicitly calling super
is the right thing to do anyway.)
一些解决方案:
让它成为。 Foo需要BaseDependency,Bar需要ExtraBaseDependency,而Foo甚至可能将该依赖包保密 - 因此Bar无法访问Foo的字段(这可能很重要如果该字段是有状态的或者涉及多个线程)。如果您认为每个类都负责声明自己的依赖关系,那么这是按预期工作的,尽管是浪费的。
如果它应该是作用域,则限制依赖项。如果BaseDependency和ExtraBaseDependency在其组件的生命周期内保持一致,则可以使用范围标记它(例如@Singleton
或@Reusable
)减少依赖项的重新创建,或者只保存并返回@Module
中的实例,如果它更适合自己管理单例实例。这不是一个好的通用解决方案,但如果这仍然是挥之不去的工作,那么你也可以用它来解决这个问题。
如果可能,切换到构造函数参数(对于您的情况,不是这样)。如果Bar扩展Foo,Bar可以有一个@Inject
- 带注释的构造函数,使用您请求的特定依赖项显式调用Foo的构造函数。当然,对于Android视图,你需要会员注入;这是您的其他案例或其他读者的选择。
从Foo.dep中删除@Inject
注释。 Foo.dep的注入显然取决于它的子类,并且字段不是多态的,所以让Foo声明它的{{1注释字段对于您使用它的方式并不安全。
将Foo重构为一个抽象基类。这也不是一切的通用解决方案,但是你可以通过设计Foo来进行子类化和公开BasicFoo来清除类层次结构的困境。 DefaultFoo实现。这将使其成为填充Foo.dep的子类责任,无论如何,这可能更容易在您的类层次结构中进行推理。