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
Dagger 2 - two provides method that provide same interface
提问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需要为Rec和Circle提供实例:
@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 Shape
instance, 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
@Qualifier
annotations 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 new
in injectedtypes, Modules are exactly the correct place to call new
if 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
@Qualifier
annotation, 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.
@Named
and 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@Parallelogram
above.
- @Named 是一种内置
@Qualifier
注释,很像我上面创建的注释。对于简单的情况,它工作得很好,但是因为绑定只是一个字符串,所以在检测有效键或自动完成键时,您不会从 IDE 获得太多帮助。 - 与 Named 的字符串参数一样,自定义限定符可以具有字符串、原始、枚举或类文字属性。对于枚举,IDE 通常可以自动完成有效值。
@Named
和自定义限定符可以通过在组件方法上指定注释以完全相同的方式从注释中访问,就像我在@Parallelogram
上面所做的那样。
回答by Bryan
I do not think it is a good idea to use the new
operator 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 @Provides
annotated methods.
我认为new
在Module
. 这将在您的对象图初始化时(即,当您调用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 ApplicationModule
should 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 Shape
classes without an @Inject
annotation, you can do so in your Component
class:
如果你需要在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 @Provides
annotated methods.
这些方法将为带@Provides
注释的方法提供的每个类提供相同的实例。
回答by Albert Vila Calvo
In addition to @Named
and custom qualifiers (shown in other responses), you can also use a custom qualifier with an enum
parameter:
除了@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
(需要字符串键,容易出错且无法自动完成)和自定义限定符(每个实现都需要一个文件)之间的混合体。