Java provides Optional to resolve null pointer exceptions. If the object wrapped around it is empty, you need to call option.isPresent () to check whether the internal instance is empty. Dagger2 provides an @bindSoptionalof annotation for injecting Optional variables.

So if the dependency of type T can be found in Component, then the Optional wrapped object is non-empty, and if the dependency can’t be found, then empty is returned.

public class BMW {
    @Inject
    public BMW(a) {
        super(a); }@Inject
    Optional<IWheel> wheel;
}
Copy the code

BMW has a variable wheel of type Optional.

@Module
abstract class WheelModel {
    @BindsOptionalOf abstract IWheel provideWheel(a);
}
Copy the code

Add the @bindSoptionalof annotation to the providWheel() method in WheelModel, and Dagger2 looks for dependencies in Component that can provide an IWheel instance.

@Component(modules = {WheelModel.class})
public interface CarComponent {
    BMW makeBmw(a);
}
Copy the code

The WheelModel Module class is relied on in Component.

Test 1:

public class CarMain {
    public static void main(String[] args) { CarComponent component = DaggerCarComponent.create(); BMW bmw = component.makeBmw(); System.out.println(bmw.wheel.isPresent()); }}Copy the code

Obviously, false is printed because no one in the Component can provide an IWheel instance.

Now change the type returned by the method in WheelModel to the concrete type BMWWheel and what happens:

@Module
abstract class WheelModel {
    @BindsOptionalOf abstract BMWWheel provideWheel(a);
}
Copy the code

BMWWheel:

public class BMWWheel implements IWheel {
    public BMWWheel(a) {
        super(a); }/ / * * *
}
Copy the code

Note that the @Inject annotation is not currently added to the constructor of the BMWWheel

Test 2:

A compilation error will occur: [Dagger/MissingBinding] java.util.Optional<com.hero.IWheel> cannot be provided without an @Inject constructor or an @Provides-annotated method.

Optional requires an object dependency with the constructor of that type annotated by @inject, or a Module class with a method that Provides an instance of that type annotated by @provides, otherwise the dependency will not be found.

Add @inject annotation to BMWWheel:

public class BMWWheel implements IWheel {
    @Inject
    public BMWWheel(a) {
        super(a); }/ / * * *
}
Copy the code

Test 3:

A compilation error will still occur: @BindsOptionalOf methods cannot return unqualified types that have an @Inject-annotated constructor because those are Always present.

Note: If the @BindSoptionalof annotation method returns a concrete type, if the constructor of this type has @Inject, then concrete instances will be injected every time. That is, @BindSoptionalof is not needed in this scenario

So what should be done?

Provide another Module class that provides the required dependencies

@Module
public class BMWWheelModule {
    @Provides
    BMWWheel provideWheel(a) {
        return newBMWWheel(); }}Copy the code

Create a BMWWheelModule that provides BMWWheel instances.

@Module
abstract class WheelModel {
    @BindsOptionalOf abstract IWheel provideWheel(a);
}
Copy the code

And rely on the new Module in the Component

@Component(modules = {WheelModel.class, BMWWheelModule.class})
public interface CarComponent {
    BMW makeBmw(a);
}
Copy the code

The test 4:

In this case the compilation succeeds, but optional.isPresent () returns false, because the new Module class provides instances of BMWWheel, not IWheel, so the dependency is still not found.

Modify the new Module class to provide an instance of IWheel:

@Module
public class BMWWheelModule {
    @Provides
    IWheel provideWheel(a) {
        return newBMWWheel(); }}Copy the code

Test 5:

When executed, option.isPresent () returns true. Since the new Module class provides IWheel dependencies, it can inject an instance of IWheel into the Optional object.