question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Support for set multibindings with component provision methods of component dependencies

See original GitHub issue

Dagger documentation says that:

The following are available as dependencies and may be used to generate a well-formed component:

  • The component provision methods of the component dependencies

And also that:

A binding in a subcomponent can depend on a multibound set or map from its parent, just as it can depend on any other binding from its parent. But a subcomponent can add elements to multibound sets or maps that are bound in its parent as well, by simply including the appropriate @Provides methods in its modules.

Would it be possible to add support for multibindings from component dependencies?

I suppose there is no support for that right now, I tried:

@Component(modules = [ModuleA::class])
interface ParentA {
    @ElementsIntoSet
    fun strings(): Set<String>
}

@Module
class ModuleA {
    @Provides
    @IntoSet
    fun string() = "A"
}

@Component(modules = [ModuleB::class])
interface ParentB {
    @ElementsIntoSet
    fun strings(): Set<String>
}

@Module
class ModuleB {
    @Provides
    @IntoSet
    fun string() = "B"
}

@Component(dependencies = [ParentA::class, ParentB::class])
interface Child {
    val strings: Set<String>
}

But I get this error:

e: com/example/dagger/ParentB.java:10: error: Multibinding annotations may only be on @Provides, @Produces, or @Binds methods
    @dagger.multibindings.ElementsIntoSet()
    ^
e: com/example/dagger/ParentA.java:10: error: Multibinding annotations may only be on @Provides, @Produces, or @Binds methods
    @dagger.multibindings.ElementsIntoSet()
    ^
e: com/example/dagger/Child.java:10: error: [Dagger/DuplicateBindings] java.util.Set<java.lang.String> is bound multiple times:
    public abstract java.util.Set<java.lang.String> getStrings();
                                                    ^
      @org.jetbrains.annotations.NotNull @dagger.multibindings.ElementsIntoSet Set<String> com.example.dagger.ParentA.strings()
      @org.jetbrains.annotations.NotNull @dagger.multibindings.ElementsIntoSet Set<String> com.example.dagger.ParentB.strings()
  
      java.util.Set<java.lang.String> is provided at
          com.example.dagger.Child.getStrings()

My use case is that I have an interface like AccountRepository and it’s implemented differently in two components. I need a component that uses both AccountRepository implementations to get all accounts from both of them.

I was trying to achieve something similar by using modules directly instead, but there were some scoping issues I wasn’t able to resolve.

Update

After some more experimenting, this idea might not be that useful after all. If ParentA and ParentB have other provision methods with overlapping types, I wouldn’t be able to set them both as dependencies anyway… But maybe in some other scenario this would be useful.

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:5

github_iconTop GitHub Comments

5reactions
lwasylcommented, Sep 26, 2019

@ronshapiro Can you please clarify how #1112 would solve this? Would I have to write e.g.

val childComponent = DaggerChildComponent.create()
val rootComponent = DaggerRootComponent.builder()
   .includeElementsFromSet(childComponent.someSet)
   .build

where includeElementsFromSet would have @ElementsIntoSet annotation, is that correct?

I’m curious because we still need to add the child component. Plus if we want to provide more than one multibinding we’ll have something like:

val childComponent = DaggerChildComponent.create()
val secondChildComponent = DaggerChildComponent.create()
val rootComponent = DaggerRootComponent.builder()
   .plusChild(childComponent)
   .plusSecondChild(secondChildComponent)
   .includeElementsFromSet(childComponent.someSet)
   .includeElementFromAnotherSet(childComponent.anotherSet)
   .includeElementFromSecondChild(secondChildComponent.alsoSet)
   .build

So for each component we’ll have to both add it to the root component and specify each multibinding provision separately. Seems like it would be pretty annoying, since we now need to capture the child component in a variable and be sure to explicitly add each multibinding provision (even though it’s already defined in the component). Any thoughts on this?

0reactions
davidxiasccommented, Jul 22, 2020

I’m interested in seeing this as well - #1112 doesn’t solve this because even in @lwasyl’s example, if there are two child components that both provide the same set, the parent can’t be constructed due to conflicting bindings of the same set type.

@Component
interface ChildA {
    val strings: Set<String>
}

@Component
interface ChildB {
    val strings: Set<String>
}
val childA = DaggerChildA.create()
val childB = DaggerChildB.create()
val rootComponent = DaggerRootComponent.builder()
    .childA(childA)
    .childB(childB) // ERROR: Multiple bindings for Set<String>
    .includeElements(childA.strings)
    .includeElements(childB.strings)
    .build()
Read more comments on GitHub >

github_iconTop Results From Across the Web

Multibindings - Dagger
Dagger allows you to bind several objects into a collection even when the objects are bound in different modules using multibindings. Dagger assembles...
Read more >
java - Is it possible to selectively set modules for components ...
You might want to consider using Multibindings, which allows for users to optionally add in dependencies into a Set<T> or Map<K,V> . Here's...
Read more >
Deep dive into Dagger generated code (part 2) - ProAndroidDev
In Dagger there are two ways to instantiate the component — Builder ... of a Builder \ Factory , provision method of a...
Read more >
Dagger 2 Multibindings Illustrated | by Elye - Medium
Check out this tutorial if you don't quite get what this means. @Component(modules = MyModule.class) interface MyComponent { Map<String, Long> ...
Read more >
Dagger by Tutorials, Chapter 13: Multibinding | Kodeco, the ...
Dependency Injection & Scopes ... Section III: Components & Scope Management ... Your first step is to use Dagger multibinding with Set to...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found