Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is my field `null` after injection? How do I inject my object?

This is a Canonical Question because there are a lot of misconceptions about object initialization with Dagger 2.

If your question was flagged as a duplicate please read this post carefully and make sure to understand the difference between constructor injection and field injection.

I try to inject a Context into my presenter, but I get a NullPointerException when trying to use it.

class MyPresenter {

  @Inject Context context;

  private MyView view;

  @Inject
  MyPresenter(MyView view) {
    this.view = view;
  }
}

My module looks like this

@Module
class MyModule {

  @Provides
  MyPresenter provideMyPresenter(MyView view) {
    return new MyPresenter(view);
  }
}

I inject the presenter in my Activity here:

class MyActivity extends Activity {

  @Inject MyPresenter presenter;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    createMyActivityComponent().inject(this);
  }
}
like image 295
David Medenjak Avatar asked May 10 '18 10:05

David Medenjak


People also ask

What does the @inject annotation do?

The annotation @Inject allows Dagger to call the constructor of the injected class to create an instance of a class, twitterApi . However, there are some cases that we may not call a constructor directly. For example: Interfaces.

Why field based injection is not recommended?

The reasons why field injection is frowned upon are as follows: You cannot create immutable objects, as you can with constructor injection. Your classes have tight coupling with your DI container and cannot be used outside of it. Your classes cannot be instantiated (for example in unit tests) without reflection.

What happens if we used both setter injection and constructor injection?

If we use both constructor and setter injection, IOC container will use the setter injection. Changes: We can easily change the value by setter injection. It doesn't create a new bean instance always like constructor. So setter injection is flexible than constructor injection.

How do you inject fields in spring?

Spring uses primarily two types of dependency injection: Constructor and Setter Injection but we can also use another injection technique called field injection. The field injection can be done by using the @Autowired annotation directly on the field.


1 Answers

The above includes both constructor and field injection, but neither done right. The example would behave the same if we removed all the @Inject annotations from MyPresenter since we're not using any of them.

@Provides
MyPresenter provideMyPresenter(MyView view) {
  // no constructor injection, we create the object ourselves!
  return new MyPresenter(view);
}

// also no mention anywhere of component.inject(presenter)
// so the fields won't be injected either

Make sure to use either constructor injection or field injection. Mixing both will usually indicate an error in your setup or understanding.

  • @Inject on a field is a marker for field injection
  • @Inject on a constructor is a marker for constructor injection

This means your class should have either of

  • a single @Inject on the constructor, or
  • a @Inject on all the fields to initialize, but none on the constructor!

Don't sprinkle @Inject everywhere and expect things to work! Make sure to place the annotation where needed. Don't mix field and constructor injection!

Constructor injection should be favored over field injection as it creates an initialized and usable object. Field injection is to be used with Framework components where the Framework creates the objects. You have to manually call component.inject(object) for field injection to be performed, or any annotated fields will be null when you try to use them.

Constructor Injection

As the name suggests you put your dependencies as parameters in the constructor. The annotation on the constructor tells Dagger about the object and it can then create the object for you by calling it with all the required dependencies. Dagger will also inject any annotated fields or methods after creating the object, but plain constructor injection should usually be favored as it doesn't hide any dependencies.

Dagger creating the object also means there is no need for a @Provides method in your module that creates the object. All you need to do is add @Inject to the constructor and declare the dependencies.

class MyPresenter {

  private Context context;
  private MyView view;

  @Inject
  MyPresenter(MyView view, Context context) {
    this.view = view;
    this.context = context
  }
}

If you want to bind your implementation to an interface, there is still no need to create the object yourself.

@Module class MyModule {

  @Provides
  MyPresenter providePresenter(MyPresenterImpl presenter) {
    // Dagger creates the object, we return it as a binding for the interface!
    return presenter;
  }
}

And there is even a shorter (and more performant) version of the above use-case:

@Module interface MyModule {

  @Binds
  MyPresenter providePresenter(MyPresenterImpl presenter)
}

Constructor injection should be your default way of using Dagger. Make sure that you don't call new yourself or you misunderstood the concept.

Field Injection

There are times when you can't use constructor injection, e.g. an Activity in Android gets created by the Framework and you shouldn't override the constructor. In this case we can use field injection.

To use field injection you annotate all the fields that you want initialized with @Inject and add a void inject(MyActivity activity) method to the component that should handle the injection.

@Component
interface MyComponent {
  void inject(MyActivity activity);
}

And somewhere in your code you have to call component.inject(myActivity) or the fields will not be initialized. e.g. in onCreate(..)

void onCreate(..) {
  // fields still null / uninitialized
  myComponent.inject(this);
  // fields are now injected!

  // ...
}

Field injection is not transitive. Just because you inject an Activity this does not mean that Dagger will also inject the fields of the presenter it injected. You have to inject every object manually, which is one reason why you should favor constructor injection.

There are tools that help mitigate the boilerplate of creating components and injecting your objects like AndroidInjection.inject() which will do this for you, but it still has to be done. Another example is AppInjector which adds various lifecycle listeners to inject your Activities and Fragments, but it will still call AndroidInjection which then creates your component and injects the object.

Make sure that you inject the object before using it and that there is no constructor annotated with @Inject to avoid confusion.

What else?

There is also the lesser used method injection and of course Dagger can't inject third party libraries, which you have to construct and provide in your modules.

like image 96
David Medenjak Avatar answered Nov 03 '22 01:11

David Medenjak