Подтвердить что ты не робот

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, и он находит два из них, поэтому он не знает, какой из них нужно вернуть.

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

4b9b3361

Ответ 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 (который требует строковых ключей, который подвержен ошибкам и не может быть заполнен автоматически) и пользовательскими квалификаторами (для которых требуется файл для каждой реализации).