0

I am familiar with the dependency inversion principle in traditional design patterns, where you have an abstract product that performs tasks in a class that requests a factory method. But dependency injection is different in that it uses annotations to generate products.

I am reviewing this Dagger 2 example. This is the end result:

// CoffeeApp.java
public class CoffeeApp {
  @Singleton
  @Component(modules = { DripCoffeeModule.class })
  public interface Coffee {
    CoffeeMaker maker();
  }

  public static void main(String[] args) {
    Coffee coffee = DaggerCoffeeApp_Coffee.builder().build();
    coffee.maker().brew();
  }
}

$ java -cp ... coffee.CoffeeApp
~ ~ ~ heating ~ ~ ~
=> => pumping => =>
 [_]P coffee! [_]P

I'm having trouble understanding this line right here:

 CoffeeMaker maker();

In the CoffeeMaker class, there is no make() method. So how does this work?

This is my understanding:

The CoffeeMaker class uses the @Inject annotation in its constructor:

@Inject CoffeeMaker(Lazy<Heater> heater, Pump pump) {
  this.heater = heater;
  this.pump = pump;
}

This will force Dagger to instantiate a new Heater and a new Pump whenever a CoffeeMaker is instantiated, is that correct? Or does the CoffeeMaker class itself just needs to be referenced? The document says this:

When you request a CoffeeMaker, it’ll obtain one by calling new CoffeeMaker() and setting its injectable fields.

BUt what does it mean by request?

When it says, it will set its injectable fields, I notice that Heater and Pump are interfaces. There is a corresponding PumpModule class with the @Module annotation. And it contains the method providePump.

The document says:

The method’s return type defines which dependency it satisfies.

Does this mean that when @Inject CoffeeMaker constructor is called, it in turn discovers the pump reference and searches for the PumpModule @Module and then discovers providePump and instantiates a new pump of type Thermosiphon?

Daniel Viglione
  • 8,014
  • 9
  • 67
  • 101
  • Please see [this question](http://stackoverflow.com/questions/41472319/what-is-the-purpose-of-the-getter-methods-in-components-in-dagger-2) for an explanation of the `maker()` method inside the Component – David Rawson Apr 05 '17 at 02:59

2 Answers2

1

Dagger is a compile-time dependency injection solution.

The line CoffeeMaker maker() on the interface CoffeeApp.Coffee, annotated as it is, means that Dagger will generate and compile an implementation of CoffeeApp.Coffee that has an implementation for the maker method which will provide you a fully-injected CoffeeMaker instance. If you'd like, you can look at the file that Dagger generates, DaggerCoffeeApp_Coffee.java, which looks a lot like the implementation on slide 41 of the original Dagger 2 talk. (Slides 35 and 36 show the corresponding Factory/Provider implementaitons Dagger writes.)

This will force Dagger to instantiate a new Heater and a new Pump whenever a CoffeeMaker is instantiated, is that correct?

Yes, usually. Though it is your prerogative to return an existing instance in a @Provides method, or use an annotation like @Singleton to let Dagger manage an existing instance, Dagger's default behavior is to create a new instance for each dependency, and create a new instance for each of the dependencies of that dependency, and so forth.

Does this mean that when @Inject CoffeeMaker constructor is called, it in turn discovers the pump reference and searches for the PumpModule @Module and then discovers providePump and instantiates a new pump of type Thermosiphon?

You are nearly correct: To produce that implementation, Dagger will inspect the @Inject CoffeeMaker constructor, discover that the CoffeeMaker needs a Heater and a Pump, and so forth. For frameworks like Guice, that does all happen at runtime, but for Dagger all of that analysis and mapping happens at compile time. In your example, the @Provides Pump method exists in DripCoffeeModule, which Dagger doesn't search for: The example provides it through the @Component annotation on CoffeeApp.Coffee.

Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
0

@Donato, you're right, CoffeeMaker doesn't have a maker() method, it's the Coffee interface that does have the maker() method. And that method (maker()) returns an object of type CoffeMaker. So in your example, DaggerCoffeeApp_Coffee.builder().build(); would return an implementation of Coffee, which in turn implements the maker() method.

Question: "BUt what does it mean by request?"

My Answer: Request means injection (i.e. wherever you see @Inject). So, for example, the following private variable declaration "requests" an instance of CoffeeMaker,

public MyClass {
  @Inject private CoffeeMaker maker;
  ...
}

And CoffeeMaker, in turn, "requests" an instance or Heater and an instance of Pump:

@Inject CoffeeMaker(Lazy<Heater> heater, Pump pump) {
  this.heater = heater;
  this.pump = pump;
}

So an instance of Heater and an instance of Pump will be instantiated and injected when instantiating (or injecting) maker.

Question: "Does this mean that when @Inject CoffeeMaker constructor is called, it in turn discovers the pump reference and searches for the PumpModule @Module and then discovers providePump and instantiates a new pump of type Thermosiphon?"

My Answer: That's right.

Khaled
  • 644
  • 1
  • 8
  • 14