Кинжал 2 – два обеспечивает метод, обеспечивающий тот же интерфейс

Скажем, у меня есть:

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) error: Shape привязывается несколько раз.

Мне кажется, что это невозможно сделать, потому что компонент пытается найти экземпляр Shape , и он находит два из них, поэтому он не знает, какой из них нужно вернуть.

Мой вопрос: как я могу справиться с этой проблемой?

Solutions Collecting From Web of "Кинжал 2 – два обеспечивает метод, обеспечивающий тот же интерфейс"

Недавно я опубликовал ответ на такой вопрос в этом сообщении:

Кинжал 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; 

Аннотации @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 , очень похожая на ту, которую я создал выше. Для простых случаев он отлично работает, но поскольку привязка – это всего лишь строка, вы не получите столько же помощи от вашей среды разработки в обнаружении правильных ключей или автозаполнения ключа.
  • Как и в случае с строковым параметром Named, пользовательские квалификаторы могут иметь свойства string, primitive, enum или class literal. Для перечислений, IDE часто могут автозаполнять допустимые значения.
  • @Named и пользовательские квалификаторы могут быть доступны из аннотаций точно так же, указав аннотацию на компонентный метод, как я уже сделал с @Parallelogram выше.

Я не думаю, что это хорошая идея использовать 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 .