Dagger 2 - 两个提供提供相同接口的方法

Dagger 2 - two provides method that provide same interface

假设我有:

public interface Shape  {}


public class Rectangle implements Shape {

}

public class Circle implements Shape {

}

我有一个 ApplicationModule 需要为 RecCircle 提供实例:

@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();
}

按原样使用代码 - 它无法编译。 错误说

Error:(33, 20) error: Shape is bound multiple times.

这对我来说是有道理的,因为组件试图找到一个 Shape 实例,它找到了其中两个,所以它不知道要 return.

我的问题是 - 我该如何处理这个问题?

@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();

旁白:尽管我同意 sector11 的观点,即您不应在 注入的 类型中使用 new,但模块正是调用 new 的正确位置如果需要的话。除了添加限定符注释外,我想说你的模块对我来说看起来很合适。


编辑 关于 @Named 与自定义限定符注释的使用:

  • @Named 是一个内置的 @Qualifier 注释,很像我在上面创建的注释。对于简单的情况,它工作得很好,但是因为绑定只是一个字符串,所以在检测有效密钥或自动完成密钥方面,您不会从 IDE 那里获得太多帮助。
  • 与 Named 的字符串参数一样,自定义限定符可以具有字符串、基元、枚举或 class 文字属性。对于枚举,IDEs 通常可以自动完成有效值。
  • @Named 和自定义限定符可以通过在组件方法上指定注释以完全相同的方式从注释访问,就像我在上面的 @Parallelogram 中所做的那样。

我最近 post 在这个 post 中回答了这样一个问题:

您需要像这样在您的模块中使用 @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

我认为在 Module 的构造函数中使用 new 运算符不是一个好主意。这将在对象图初始化时(即当您调用 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();
    }

}

有了这个,您可以像这样将这些对象注入到您的 classes 中:

@Inject @RectangleShape /* @Named("rectangle") */ public Shape mRectangle;
@Inject @CircleShape /* @Named("circle") */ public Shape mCircle;

如果您需要提供没有 @Inject 注释的 Shape class 实例,您可以在 Component class 中这样做:

@Component(modules = { ApplicationModule.class })
public interface ApplicationComponent {

    void inject(MyApplication application);

    @RectangleShape // @Named("rectangle")
    Shape getRectangle();

    @CircleShape // @Named("circle")
    Shape getCircle();

}

这些方法将提供 @Provides 注释方法提供的每个 class 的相同实例。

除了 @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(需要字符串键,容易出错且无法自动完成)和自定义限定符(每个实现都需要一个文件)之间的混合体。