32

I have a question regarding Android Dagger 2 und the usage of @Inject and @Provide annotations. Given are the following two simplified examples:

public class A {
  String msg;

  public A(String msg){
    this.msg = msg;
  }
}

public class B {
  public A a;

  public B(A a){
    this.a = a;
  }
}

@Module
public class AModule {
  @Provides
  A providesA(){
    return new A("blah");
  }

  @Provides
  B ProvidesB(A a)
  {
    return new B(a);
  }
}

The example is pretty straight forward, I have two methods in my AModule with @Provides annotations. Therefore, Dagger can create an object of B using an instance of A with the string blah.

My second example looks like this:

public class A {
  String msg;

  public A(String msg){
    this.msg = msg;
  }
}

public class B {
  public A a;

  @Inject
  public B(A a){
    this.a = a;
  }
}

@Module
public class AModule {
  @Provides
  A providesA(){
    return new A("blah");
  }
}

In this example, Dagger can create an instance of B because an object A can be created using AModule. The instance of B can be created because it's constructor uses the @Inject annotation.

So my question is: What's the difference between those two examples? Both seem to have the same semantics. Does the generated code differ and are there any pitfalls? Or is it just a matter or personal taste or best practices?

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Christian Ammann
  • 888
  • 8
  • 19

1 Answers1

30

They work similarly, and the @Inject style is preferred when you have such an easy choice like in your example. However, this isn't always the case:

  • If B, which consumes A, is not under your control and not DI-aware, you will have no way to add the @Inject annotation.
  • If B is an interface, you will need @Provides (or @Binds in newer Dagger versions) to identify which implementation to use.
  • If you choose not to use your Dagger object graph for every injected parameter, you can call the constructor manually whether it is marked @Inject or not. This might be the case if you want a specific instance or constant as a constructor parameter, but you can't or don't want to set up the binding for the whole object graph.

@Provides allows you to effectively create a factory method, with everything that allows. This is a great way to change which instances are included in your graph, or to effectively add to the class's constructor graph-wide if you can't (or shouldn't) change the class itself.

  • You can return an existing instance rather than a new one. Note that custom scopes (implemented in Dagger through subcomponents) might be a better fit for common cases, but if you have more complex instance control or use a third-party library that controls instances, you could put that into your factory method.
  • If you want your binding to return null sometimes that logic can live in a @Provides method. Make sure you annotate the injection sites as @Nullable.
  • You can choose between implementations with a factory method like a @Provides method, particularly if the choice depends on runtime environment.
  • You can run post-construction logic, including instance registration or initialization.
Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
  • to me another advantage of `@Provides` is that, I can go to a module and very easily determine what objects are being introduced in the graph, which kind of gives a birds eye view of dp graph, with `@Inject` you have to look into the specific file. – mightyWOZ Feb 17 '21 at 07:54
  • @mightyWOZ True, but you also violate DRY a little by repeating the constructor arguments in both the class constructor and its invocation in the module—and if you're responsible for injecting all those arguments from the graph, then you duplicate it a third time in the `@Provides` method arguments. If you have reason to have a clear understanding of exactly what's in your graph, I agree that using `@Provides` can help keep things clear, but if you intend to use a DI framework to reduce boilerplate and abstract away those dependencies then it may be counterproductive to do so. – Jeff Bowman Feb 17 '21 at 17:14