我想说:
public interface Shape {}
public class Rectangle implements Shape {
}
public class Circle implements Shape {
}
我有 ApplicationModule ,需要为 Rec 和圈提供实例:
@Module
public class ApplicationModule {
private Shape rec;
private Shape circle;
public ApplicationModule() {
rec = new Rectangle();
circle= new Circle ();
}
@Provides
public Shape provideRectangle() {
return rec ;
}
@Provides
public Shape provideCircle() {
return circle;
}
}
和 ApplicationComponent :
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
Shape provideRectangle();
}
代码的方式 - 它不会编译。 错误说
错误:(33,20)错误:形状被多次绑定。
对我来说这是不可能的,因为组件正在尝试查找Shape
实例,并且它找到了其中的两个,因此它不知道返回哪一个。 / p>
我的问题是 - 我该如何处理这个问题?
答案 0 :(得分:31)
我最近在这篇文章中回答了这样的问题:
Dagger 2 : error while getting a multiple instances of same object with @Named
您需要在模块中使用@Named("someName")
,如下所示:
@Module
public class ApplicationModule {
private Shape rec;
private Shape circle;
public ApplicationModule() {
rec = new Rectangle();
circle= new Circle ();
}
@Provides
@Named("rect")
public Shape provideRectangle() {
return rec ;
}
@Provides
@Named("circle")
public Shape provideCircle() {
return circle;
}
}
然后,无论你需要注射它们,只需写下
@Inject
@Named("rect")
Shape objRect;
它很有趣,但你必须以不同的方式注入Kotlin:
@field:[Inject Named("rect")]
lateinit var objRect: Shape
答案 1 :(得分:12)
@Qualifier
注释是区分具有相同类型的不同实例或注入请求的正确方法。主要用户指南页面有a whole section on them。
@Qualifier @Retention(RUNTIME)
public interface Parallelogram {} /* name is up to you */
// In your Module:
@Provides @Parallelogram
public Shape provideRectangle() {
return rec ;
}
// In your other injected types:
@Inject @Parallelogram Shape parallelogramShape;
// or
@Inject @Parallelogram Provider<Shape> parallelogramShapeProvider;
// In your Component:
@Parallelogram Shape provideRectangle();
除此之外:虽然我同意第11部分您不应该在注入类型中使用new
,但如果需要,模块恰好是调用new
的正确位置。除了添加限定符注释之外,我还说你的模块对我来说是正确的。
编辑:
@Qualifier
注释,非常类似于我上面创建的注释。对于简单的情况,它工作得很好,但由于绑定只是一个字符串,因此您无法从IDE中获得有关检测有效密钥或自动完成密钥的帮助。@Named
可以通过在组件方法上指定注释,以完全相同的方式从注释中访问自定义限定符,正如我上面的@Parallelogram
所做的那样。答案 2 :(得分:6)
我认为在new
的构造函数中使用Module
运算符并不是一个好主意。这将在初始化对象图形时(即,当您调用new ApplicationModule()
时)创建每个提供的对象的实例,而不是在Dagger第一次需要对象时。在这种情况下(只有两个对象),它可以忽略不计,但在较大的项目中,这可能会导致应用程序启动时出现瓶颈。相反,我会遵循@ sector11的建议,并在@Provides
带注释的方法中实例化你的对象。
至于提供两个相同类型的对象,@ Jeff和@Amir都是正确的。您可以使用提供的@Named()
限定符,也可以创建自己的限定符,如下所示:
@Qualifier @Retention(RetentionPolicy.RUNTIME)
public @interface RectangleShape {}
@Qualifier @Retention(RetentionPolicy.RUNTIME)
public @interface CircleShape {}
然后你的ApplicationModule
应该是这样的:
@Module
public class ApplicationModule {
@Provides @RectangleShape // @Named("rectangle")
public Shape provideRectangle() {
return new Rectangle();
}
@Provides @CircleShape // @Named("circle")
public Shape provideCircle() {
return new Circle();
}
}
有了这个,您可以将这些对象注入到您的类中:
@Inject @RectangleShape /* @Named("rectangle") */ public Shape mRectangle;
@Inject @CircleShape /* @Named("circle") */ public Shape mCircle;
如果您需要在没有Shape
注释的情况下提供@Inject
课程的实例,则可以在Component
课程中执行此操作:
@Component(modules = { ApplicationModule.class })
public interface ApplicationComponent {
void inject(MyApplication application);
@RectangleShape // @Named("rectangle")
Shape getRectangle();
@CircleShape // @Named("circle")
Shape getCircle();
}
这些方法将提供@Provides
注释方法提供的每个类的相同实例。
答案 3 :(得分:2)
除了@Named
和自定义限定符(在其他回复中显示)之外,您还可以使用带有 enum
参数的自定义限定符:
// Definition
@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface ShapeType {
ShapeTypeEnum value(); /* default ShapeTypeEnum.RECTANGLE; */
}
public enum ShapeTypeEnum {
RECTANGLE, CIRCLE
}
// Usage
@Provides @ShapeType(ShapeTypeEnum.RECTANGLE)
public Shape provideRectangle() {
return new Rectangle();
}
@Inject @ShapeType(ShapeTypeEnum.RECTANGLE) Shape rectangle;
这是@Named
(需要字符串键,容易出错且无法自动完成)和自定义限定符(每个实现需要一个文件)之间的混合。