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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/39953933/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 04:48:24  来源:igfitidea点击:

Dagger 2 - two provides method that provide same interface

javaandroiddependency-injectiondagger-2dagger

提问by Ofek Agmon

lets say I have:

可以说我有:

public interface Shape  {}


public class Rectangle implements Shape {

}

public class Circle implements Shape {

}

and I have a ApplicationModulewhich needs to provides instances for both Recand Circle:

我有一个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;
    }
}

and ApplicationComponent:

应用程序组件

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

with the code the way it is - it won't compile. error saying

使用代码的方式 - 它不会编译。错误说

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

错误:(33, 20) 错误:形状被多次绑定。

It makes sense to me that this can't be done, because the component is trying to find a Shapeinstance, and it finds two of them, so it doesn't know which one to return.

对我来说,这无法完成是有道理的,因为组件正在尝试查找一个Shape实例,并且它找到了其中的两个,因此它不知道要返回哪个。

My question is - how can I handle this issue?

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

回答by Amir Ziarati

I recently post the answer to a question like this in this post :

我最近在这篇文章中发布了这样一个问题的答案:

Dagger 2 : error while getting a multiple instances of same object with @Named

Dagger 2:使用@Named 获取同一对象的多个实例时出错

You need to use @Named("someName")in your module like this:

您需要@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;
}

}

}

Then wherever you need to inject them just write

然后在任何需要注入它们的地方只写

@Inject
@Named("rect")
 Shape objRect;

its funny but you have to inject in a different way in Kotlin:

这很有趣,但你必须在 Kotlin 中以不同的方式注入:

@field:[Inject Named("rect")]
lateinit var objRect: Shape

回答by Jeff Bowman

@Qualifierannotations are the right way to distinguish different instances or injection requests that have the same type. The main User's Guide page has a whole section on them.

@Qualifier注释是区分具有相同类型的不同实例或注入请求的正确方法。主用户指南页面有一个完整的部分

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

Aside: Though I agree with sector11 that you shouldn't use newin injectedtypes, Modules are exactly the correct place to call newif needed. Aside from adding the qualifier annotations, I'd say your Module looks just right to me.

旁白:虽然我同意sector11 不应该new注入类型中使用,但new如果需要,模块正是调用的正确位置。除了添加限定符注释之外,我想说您的模块对我来说恰到好处。



EDITregarding the use of @Named compared to custom qualifier annotations:

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

  • @Named is a built-in @Qualifierannotation, much like the one I've created above. For simple cases, it works great, but because the binding is just a string you won't get as much help from your IDE in detecting valid keys or autocompleting the key.
  • Like with Named's string parameter, custom qualifiers can have string, primitive, enum, or class literal properties. For enums, IDEs can often autocomplete valid values.
  • @Namedand custom qualifiers can be accessed from annotations in exactly the same way by specifying the annotation on the component method, as I've done with @Parallelogramabove.
  • @Named 是一种内置@Qualifier注释,很像我上面创建的注释。对于简单的情况,它工作得很好,但是因为绑定只是一个字符串,所以在检测有效键或自动完成键时,您不会从 IDE 获得太多帮助。
  • 与 Named 的字符串参数一样,自定义限定符可以具有字符串、原始、枚举或类文字属性。对于枚举,IDE 通常可以自动完成有效值。
  • @Named和自定义限定符可以通过在组件方法上指定注释以完全相同的方式从注释中访问,就像我在@Parallelogram上面所做的那样。

回答by Bryan

I do not think it is a good idea to use the newoperator within the constructor of the Module. This would create an instance of each of your provided objects upon initialization of your object graph (i.e. when you call new ApplicationModule()) instead of when Dagger needs the object for the first time. In this case (with only two objects), it would be negligible, but in larger projects this could cause a bottleneck upon the start of the application. Instead, I would follow the suggestion by @sector11, and instantiate your objects in the @Providesannotated methods.

我认为newModule. 这将在您的对象图初始化时(即,当您调用new ApplicationModule())而不是 Dagger 第一次需要该对象时,为您提供的每个对象创建一个实例。在这种情况下(只有两个对象),它可以忽略不计,但在较大的项目中,这可能会导致应用程序启动时出现瓶颈。相反,我会遵循@sector11 的建议,并在带@Provides注释的方法中实例化您的对象。

As for providing two objects of the same type, both @Jeff and @Amir are correct. You can either use the provided @Named()qualifier, or create your own qualifiers, like so:

至于提供两个相同类型的对象,@Jeff 和@Amir 都是正确的。您可以使用提供的@Named()限定符,也可以创建自己的限定符,如下所示:

@Qualifier @Retention(RetentionPolicy.RUNTIME)
public @interface RectangleShape {}

@Qualifier @Retention(RetentionPolicy.RUNTIME)
public @interface CircleShape {}

Then your ApplicationModuleshould look like this:

那么你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();
    }

}

With this you can inject these objects into your classes like this:

有了这个,您可以将这些对象注入到您的类中,如下所示:

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


If you need to provide the instances of your Shapeclasses without an @Injectannotation, you can do so in your Componentclass:

如果你需要在Shape没有@Inject注释的情况下提供你的类的实例,你可以在你的Component类中这样做:

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

    void inject(MyApplication application);

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

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

}

These methods will provide the same instance of each class provided by the @Providesannotated methods.

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

回答by Albert Vila Calvo

In addition to @Namedand custom qualifiers (shown in other responses), you can also use a custom qualifier with an enumparameter:

除了@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;

This is an hybrid between @Named(which requires String keys, which is error-prone and can't be auto-completed) and custom qualifiers (which requires a file for each implementation).

这是@Named(需要字符串键,容易出错且无法自动完成)和自定义限定符(每个实现都需要一个文件)之间的混合体。