Ritesh Shakya
Ritesh Shakya

Reputation: 585

Dependency Injection - Dagger2 - Generics

I am having problems injecting a generic type interface. Not sure how to do this or google it since I don't know the exact terms to search for. Sorry if i'm completely wrong just getting started with dagger.

Basically I have a use case class

public class LoadConversations<C extends IConversation>
    extends UseCase<List<C>, LoadConversations.Type> {

    private final IConversationRepository<C> messageRepository;

    @Inject LoadConversations(@NonNull IConversationRepository<C> messageRepository) {
        this.messageRepository = messageRepository;
    }

    ....

    public enum Type {
        ALL, NEWER, OLDER
    }
}

With IConversationRepository being an interface.

public interface IConversationRepository<C extends IConversation> {
    Observable<List<C>> conversations(LoadConversations.Type params);
}

IConversation being a blank interface and ConversationModule where i provide the IConversationRepository.

Im having problems injecting with the following code. Am i missing something or doing something completey wrong. Thanks in advance.

Trying to provide as follows:

@Provides IConversationRepository<Conversation> provideConversationRepository(
        ConversationRepository conversationRepository) {
    return conversationRepository;
}

And I'm trying to inject this to my presenter as

private final LoadConversations<Conversation> loadConversations;

@Inject public ConversationListPresenter(LoadConversations<Conversation> loadConversations) {
    this.loadConversations = loadConversations;
}

Implementation of ConversationRepository

public class ConversationRepository implements IConversationRepository<Conversation> {
    @Override public Observable<List<Conversation>> conversations(LoadConversations.Type params) {
        ....
    }
}

Error Log:

Error:(15, 10) error: com.rbttalk.android.data.repository.ConversationRepository cannot be provided without an @Inject constructor or from an @Provides-annotated method.
com.rbttalk.android.data.repository.ConversationRepository is injected at
com.rbttalk.android.di.module.sub_modules.ConversationModule.provideConversationRepository(conversationRepository)
com.rbttalk.android.domain.repository.IConversationRepository<com.rbttalk.android.domain.models.Conversation> is injected at
com.rbttalk.android.domain.usecase.conversation.LoadConversations.<init>(arg0, …)
com.rbttalk.android.domain.usecase.conversation.LoadConversations<com.rbttalk.android.domain.models.Conversation> is injected at
com.rbttalk.android.ui.main.conversation.ConversationListPresenter.<init>(loadConversations)
com.rbttalk.android.ui.main.conversation.ConversationListPresenter is injected at
com.rbttalk.android.ui.main.conversation.ConversationListFragment.userListPresenter
com.rbttalk.android.ui.main.conversation.ConversationListFragment is injected at
com.rbttalk.android.di.component.ConversationComponent.inject(conversationListFragment)

Upvotes: 3

Views: 2383

Answers (1)

Jeff Bowman
Jeff Bowman

Reputation: 95634

You're very close! The error message says it all:

com.rbttalk.android.data.repository.ConversationRepository cannot be provided without an @Inject constructor or from an @Provides-annotated method.

Note that this is not IConversationRepository; you've provided a binding for that with your @Provides method (which you can eventually consider converting to a @Binds method). However, that @Provides method has a parameter, ConversationRepository, which effectively asks Dagger to create an instance of that concrete ConversationRepository type for you. You've made that binding correctly, but now Dagger needs to instantiate ConversationRepository for you, and it simply doesn't know how.

You'll need to create an @Inject-annotated constructor for ConversationRepository using the annotation type javax.inject.Inject, even if it just looks like this:

@Inject ConversationRepository() {}

This allows Dagger to know that yes, it is safe to call that constructor. (This differs from Guice, which was willing to call a public parameterless constructor including the default constructor provided by Java.) Though you are welcome to accept injector-provided parameters in that annotated constructor (which might be nice if your repository has dependencies, because then you can keep the fields final), you may also choose to simply annotate some fields with @Inject and let the injector populate those after creation.

Upvotes: 4

Related Questions