web-dev-qa-db-fra.com

Dague 2 - deux fournit une méthode qui fournit la même interface

disons que j'ai:

public interface Shape  {}


public class Rectangle implements Shape {

}

public class Circle implements Shape {

}

et j'ai un ApplicationModule qui doit fournir des instances à la fois Rec et 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;
    }
}

et ApplicationComponent:

@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
    Shape provideRectangle();
}

avec le code tel qu'il est - il ne compilera pas. erreur en disant

Erreur: (33, 20) erreur: la forme est liée plusieurs fois.

Cela me semble logique que cela ne puisse pas être fait, car le composant essaie de trouver une instance Shape, et il en trouve deux, donc il ne sait pas laquelle retourner.

Ma question est - comment puis-je gérer ce problème?

29
Ofek Agmon

J'ai récemment posté la réponse à une question comme celle-ci dans cet article:

Dague 2: erreur lors de l'obtention de plusieurs instances du même objet avec @Named

Vous devez utiliser @Named("someName") dans votre module comme ceci:

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

}

Ensuite, partout où vous devez les injecter, écrivez

@Inject
@Named("rect")
 Shape objRect;

c'est drôle mais il faut injecter différemment dans Kotlin:

@field:[Inject Named("rect")]
lateinit var objRect: Shape
40
Amir Ziarati

Les annotations @Qualifier Sont le bon moyen de distinguer différentes instances ou demandes d'injection qui ont le même type. La page principale du Guide de l'utilisateur a ne section entière à leur sujet .

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

En plus: Bien que je convienne avec sector11 que vous ne devez pas utiliser les types new dans injected, les modules sont exactement le bon endroit pour appeler new si nécessaire. Mis à part l'ajout des annotations de qualificatif, je dirais que votre module me convient parfaitement.


[~ # ~] modifiez [~ # ~] concernant l'utilisation de @Named par rapport aux annotations de qualificatif personnalisé:

  • @Named est une annotation @Qualifier Intégrée, un peu comme celle que j'ai créée ci-dessus. Pour les cas simples, cela fonctionne très bien, mais parce que la liaison n'est qu'une chaîne, vous n'obtiendrez pas autant d'aide de votre IDE pour détecter les clés valides ou compléter automatiquement la clé.
  • Comme avec le paramètre de chaîne de Named, les qualificateurs personnalisés peuvent avoir des propriétés littérales de chaîne, primitives, enum ou de classe. Pour les énumérations, les IDE peuvent souvent compléter automatiquement des valeurs valides.
  • @Named Et les qualificateurs personnalisés sont accessibles à partir d'annotations exactement de la même manière en spécifiant l'annotation sur la méthode du composant, comme je l'ai fait avec @Parallelogram Ci-dessus.
12
Jeff Bowman

Je ne pense pas que ce soit une bonne idée d'utiliser l'opérateur new dans le constructeur de Module. Cela créerait une instance de chacun de vos objets fournis lors de l'initialisation de votre graphique d'objet (c'est-à-dire lorsque vous appelez new ApplicationModule()) au lieu de quand Dagger a besoin de l'objet pour la première fois. Dans ce cas (avec seulement deux objets), ce serait négligeable, mais dans des projets plus importants, cela pourrait provoquer un goulot d'étranglement au démarrage de l'application. Au lieu de cela, je suivrais la suggestion de @ sector11 et instancierais vos objets dans les méthodes annotées @Provides.

Quant à fournir deux objets du même type, @Jeff et @Amir sont corrects. Vous pouvez soit utiliser le qualificatif @Named() fourni, soit créer vos propres qualificatifs, comme ceci:

@Qualifier @Retention(RetentionPolicy.RUNTIME)
public @interface RectangleShape {}

@Qualifier @Retention(RetentionPolicy.RUNTIME)
public @interface CircleShape {}

Votre ApplicationModule devrait alors ressembler à ceci:

@Module
public class ApplicationModule {

    @Provides @RectangleShape // @Named("rectangle")
    public Shape provideRectangle() {
        return new Rectangle();
    }

    @Provides @CircleShape // @Named("circle")
    public Shape provideCircle() {
        return new Circle();
    }

}

Avec cela, vous pouvez injecter ces objets dans vos classes comme ceci:

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

Si vous devez fournir les instances de vos classes Shape sans annotation @Inject, Vous pouvez le faire dans votre classe Component:

@Component(modules = { ApplicationModule.class })
public interface ApplicationComponent {

    void inject(MyApplication application);

    @RectangleShape // @Named("rectangle")
    Shape getRectangle();

    @CircleShape // @Named("circle")
    Shape getCircle();

}

Ces méthodes fourniront la même instance de chaque classe fournie par les méthodes annotées @Provides.

6
Bryan

En plus de @Named et des qualificatifs personnalisés (indiqués dans d'autres réponses), vous pouvez également utiliser un qualificatif personnalisé avec un paramètre 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;

Ceci est un hybride entre @Named (qui nécessite des clés de chaîne, qui sont sujettes aux erreurs et ne peuvent pas être remplies automatiquement) et des qualificatifs personnalisés (qui nécessitent un fichier pour chaque implémentation).

2
Albert Vila Calvo