Dagger 2 - two предоставляет метод, обеспечивающий тот же интерфейс
скажет, что у меня есть:
public interface Shape {}
public class Rectangle implements Shape {
}
public class Circle implements Shape {
}
и у меня есть 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;
}
}
и ApplicationComponent:
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
Shape provideRectangle();
}
с кодом так, как он есть - он не будет компилироваться.
ошибка, говоря
Ошибка: (33, 20): Форма привязана несколько раз.
Мне кажется, что этого не может быть сделано, потому что компонент пытается найти экземпляр Shape
, и он находит два из них, поэтому он не знает, какой из них нужно вернуть.
Мой вопрос: как я могу справиться с этой проблемой?
Ответы
Ответ 1
Я недавно опубликовал ответ на такой вопрос в этом посте:
Кинжал 2: ошибка при получении нескольких экземпляров одного и того же объекта с @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
Ответ 2
@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();
Кроме того: хотя я согласен с сектором11, что вы не должны использовать new
в инъецируемых типах, Модули являются точным местом для вызова new
, если это необходимо. Помимо добавления аннотаций к классификатору, я бы сказал, что ваш модуль выглядит правильно для меня.
EDIT относительно использования @Named по сравнению с пользовательскими аннотаторами спецификаторов:
- @Named - это встроенная аннотация
@Qualifier
, очень похожая на ту, которую я создал выше. Для простых случаев он отлично работает, но поскольку привязка - это всего лишь строка, вы не получите столько же помощи от вашей среды IDE при обнаружении правильных ключей или автозавершения ключа.
- Как и с параметром Named string, пользовательские квалификаторы могут иметь свойства строки, примитива, перечисления или класса. Для перечислений, IDE могут часто автозаполнять допустимые значения.
-
@Named
и пользовательские квалификаторы могут быть доступны из аннотаций точно таким же образом, указав аннотацию метода компонента, как это было сделано выше с @Parallelogram
.
Ответ 3
Я не думаю, что это хорошая идея использовать оператор new
внутри конструктора Module
. Это создало бы экземпляр каждого из ваших предоставленных объектов при инициализации вашего графа объектов (т.е. При вызове new ApplicationModule()
) вместо того, чтобы Кинжал нуждался в объекте в первый раз. В этом случае (только с двумя объектами) это было бы незначительно, но в крупных проектах это могло бы стать узким местом в начале приложения. Вместо этого я буду следовать предложению @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
.
Ответ 4
В дополнение к @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
(который требует строковых ключей, который подвержен ошибкам и не может быть заполнен автоматически) и пользовательскими квалификаторами (для которых требуется файл для каждой реализации).