2016-10-10 17 views
14

可以说我有:匕首2 - 2提供方法,该方法提供相同的接口

public interface Shape {} 


public class Rectangle implements Shape { 

} 

public class Circle implements Shape { 

} 

和我有一个ApplicationModule其中需要提供实例两者建议

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

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

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

这是有道理的,我认为这不能做,因为组件试图找到一个Shape实例,并发现他们两个,所以它不知道哪一个返回。

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

+1

您可以使用'@ Qualifier'来区分不同的类型。 [Here](http://frogermcs.github.io/dependency-injection-with-dagger-2-the-api/)是如何使用此注释的简短示例。另外[这个SO问题](http://stackoverflow.com/questions/35829544/dagger2-custom-qualifier-usage)也许有用。 – QBrute

+1

@ ofek-agmon在使用di概念时,不要在构造函数定义中使用new,而应该提供和提供新Object的方法,并且这些对象将在相应的构造函数中注入。 – sector11

+0

感谢您的回答。@ sector11你能指点我一个示例代码来看看你的意思吗? –

回答

19

我最近张贴的答案,在这个岗位是这样一个问题:

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; 
+0

谢谢,工作很棒! –

+0

显然,这段代码编译得很好,但注入实际上不起作用。我在这里提出了一个新的问题,如果你看一看,会很感激。 http://stackoverflow.com/q/40139428/1662033 –

8

@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的字符串参数一样,自定义限定符可以具有字符串,基元,枚举或类文字属性。对于枚举,IDE通常可以自动完成有效值。
  • @Named和定制限定符可以通过在组件方法上指定注释完全相同的方式从注释中访问,就像我上面所做的@Parallelogram一样。
+0

感谢您的回答。我会阅读有关限定符的注释,但是Amir Ziarati的回答也不好吗?我只是现在添加到@Named(“rect”)并且它工作的很好,但是我只会在使用@Inject注释注入时才起作用,而在像下面这样在组件中创建提供方法时不起作用:'Shape provideCircle() ;','形成provideRect();'然后你可以这样做:'Circle = myComponent.provideCircle();'。我认为@Named(“rect”)会遇到这种情况。你怎么看? –

+0

@Ofek对于“是不是Amir Ziarati的回答也很好”,这是一个公平的答案;我没有解决他的问题,因为他在我发布我的时候并不存在。我已经编辑了我的答案,以解决'@ Named'的差异,但总之它们都应该可以正常工作。自定义限定符是一些额外的代码,但也可以提供更多的IDE帮助和类型安全。 –

+0

明白了。谢谢! –

3

我不认为在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(); 
    } 

} 

有了这个,你可以注入这些对象为您的类像这样:

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

如果您需要提供图片IDE您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注解的方法提供的每个类的同一个实例。 (在其它响应示出)

+0

我明白你在@Porvides方法中创建类的含义,而不是在构造函数中防止出现瓶颈。但是,例如,我也有一个SharedPrefs类,它是在ApplicationModule中创建的,我使用'applicationComponent.provdieSharedPrefs();' - >在多个类中使用它''所以,如果我在@提供方法 - >我将有很多来自同一个类的实例。 –

+0

@OfekAgmon你提供给Dagger的'Component'是一个'interface',你可以在其中放置getter方法以这种方式提供你的类的实例。因此,如果您在返回“SharedPreferences”实例的ApplicationComponent中创建了一个getSharedPreferences()方法,Dagger将始终为您的ApplicationModule提供'SharedPreferences'实例。不需要在'@Proced'注释方法之外创建'SharedPreferences'实例。 – Bryan

0

除了@Named和定制限定符,也可以使用自定义限定符具有enum参数:

// Definition 

@Qualifier 
@Documented 
@Retention(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之间的杂交体(其需要字符串键,容易出错并且不能自动完成)和自定义限定符(每个实现需要一个文件)。