Addev
Addev

Reputation: 32243

Dagger: class could not be bound with key

I'm trying for first time dagger and, after configuring the module, I get an error I don't understand.

My code is:

public class Parrot {

    private Language language;

    @Inject
    public Parrot(Language language) {
        this.language = language;
    }

    public void sayHello() {
        System.out.println(language.getSalute());
    }
}

public interface Language {
    public String getSalute();
}

public class EnglishLanguage implements Language {

    @Override
    public String getSalute() {
        return "Hello!";
    }
}

My module is

@Module(
        injects = Language.class
)
public class PetShopModule {

    @Provides Parrot provideParrot(Parrot parrot){
        return parrot;
    }
}

And in the code I use it this way

EnglishLanguage lang=SystemApp.getSystemLanguage();
ObjectGraph objectGraph = ObjectGraph.create(new PetShopModule());
objectGraph.inject(myLanguage);
Parrot parrot = objectGraph.get(Parrot.class);

The compiler complains with:

error: com.example.petshop.Language could not be bound 
with key com.example.petshop.Language required by com.example.petshop.PetShopModule 
for com.example.petshop.PetShopModule

What do I am doing wrong?

Note: This is only a simplified example, in the real code the EnglishLanguage is a system class, and I can't modify nor create it, just get a reference

Upvotes: 7

Views: 2888

Answers (2)

Jake Lee
Jake Lee

Reputation: 7989

The existing answer is perfect, but didn't solve my obscure case.

If you have a base Activity with a couple of DI helper classes, make sure your Activity that extends this does so properly!

Base Activity class:

  private fun initializeObjectGraph() {
    activityObjectGraph = (application as App).getObjectGraph()
        .plus(ActivityModule(this))
        .plus(*getAdditionalModulesForObjectGraph())
  }

  protected open fun getAdditionalModulesForObjectGraph() = arrayOf<Any>()

  abstract fun injectDependencies(activityObjectGraph: ObjectGraph): LifecycleReceiver

Overrides needed in extension Activity:

  override fun injectDependencies(activityObjectGraph: ObjectGraph): LifecycleReceiver {
    activityObjectGraph.plus(MyModule(this)).inject(this)
    return DummyLifecycleReceiver
  }

  override fun getAdditionalModulesForObjectGraph(): Array<Any> = arrayOf(MyModule(this))

Upvotes: 0

nhaarman
nhaarman

Reputation: 100448

Instead of commenting on what you're doing wrong, let's give the correct example, and explain what's happening there.

This snippet is perfect, and stays as it is:

public class Parrot {

    private Language language;

    @Inject
    public Parrot(Language language) {
        this.language = language;
    }

    public void sayHello() {
        System.out.println(language.getSalute());
    }
}

public interface Language {
    public String getSalute();
}

public class EnglishLanguage implements Language {

    @Override
    public String getSalute() {
        return "Hello!";
    }
}

With the @Inject annotation on the Parrot constructor, you're telling Dagger, "Whenever I request an instance of Parrot, use this constructor to instantiate it".

Dagger sees that Parrot needs an instance of the Language interface, and tries to find a way to retrieve it. However, since Language is an interface, Dagger needs to know which concrete type to use.

Since you cannot edit the EnglishLanguage class, you'll need to write a @Provider method for it:

@Module
public class PetshopModule {

  @Provides
  Language provideLanguage() {
    return SystemApp.getSystemLanguage();
  }
}

Now, you should be able to get an instance of Parrot out of your ObjectGraph, like this:

ObjectGraph graph = ObjectGraph.create(new PetshopModule());
Parrot parrot = graph.get(Parrot.class);

I have not tested this, bit this should work.


If you were able to modify the EnglishLanguage class, you could do the following. Annotate the EnglishLanguage constructor with @Inject, and request an EnglishLanguage instance in the Language provider method:

public class EnglishLanguage implements Language {

  @Inject
  public EnglishLanguage() {
  }

  @Override
  public String getSalute() {
    return "Hello!";
  }
}

@Module
public class PetshopModule {

  @Provides
  Language provideLanguage(EnglishLanguage language) {
    return language;
  }
}

In this case, Dagger looks for the Language provider method, and instantiates an EnglishLanguage to return.

Upvotes: 14

Related Questions