Dagger: error: A @Module may not contain both non-static and abstract binding methods

Created on 10 Dec 2019  路  2Comments  路  Source: google/dagger

I have the following mess:

@Module
abstract class MyModule {
    @PerScreen
    @ContributesAndroidInjector
    abstract fun fragment(): MyFragmentImpl

    @Binds
    @IntoMap
    @ClassKey(MyScreen::class)
    abstract fun viewModel(viewModel: MyViewModel): ViewModel

    @Provides
    @IntoMap
    @ClassKey(MyScreen::class)
    fun fragmentBuilder(): FragmentBuilder<*, *> = MyFragmentImpl.builder()
}

I get the following error:

error: A @Module may not contain both non-static and abstract binding methods

Clearly, the issue is having both @Binds and @Provides in the same class.


I had to replace it with

@Module(includes = [MyFragmentBuilderModule::class])
abstract class MyModule {
    @PerScreen
    @ContributesAndroidInjector
    abstract fun fragment(): MyFragmentImpl

    @Binds
    @IntoMap
    @ClassKey(MyScreen::class)
    abstract fun viewModel(viewModel: MyViewModel): ViewModel
}

@Module
internal class MyFragmentBuilderModule {
    @Provides
    @IntoMap
    @ClassKey(MyScreen::class)
    fun fragmentBuilder(): FragmentBuilder<*, *> = MyFragmentImpl.builder()
}

I feel that as fun fragmentBuilder() is public, therefore Dagger should be able to inherit that method, without me having to declare two modules instead of 1.

This limitation feels like something that Dagger should be able to support.


Though maybe I just need to add a companion object and a @JvmStatic, I'll look into that.

EDIT: That gives me

@Provides methods can only be present within a @Module or @ProducerModule

because the companion object is not a @Module. I guess that settles it.

Most helpful comment

Hmm, I guess that explains it.

I guess the best thing I can come up with in that case is

@Module(includes = [MyFragmentBuilderModule::class])
abstract class MyModule {
    @PerScreen
    @ContributesAndroidInjector
    abstract fun fragment(): MyFragmentImpl

    @Binds
    @IntoMap
    @ClassKey(MyScreen::class)
    abstract fun viewModel(viewModel: MyViewModel): ViewModel
}

@Module
internal object MyFragmentBuilderModule {
    @Provides
    @IntoMap
    @ClassKey(MyScreen::class)
    @JvmStatic
    fun fragmentBuilder(): FragmentBuilder<*, *> = MyFragmentImpl.builder()
}

Not ideal, would have preferred to make the definitions in one module, but I'll have to accept it for now, then.

The plan is to kill these Impl.builder()s anyway.

Thanks for the quick answer, by the way. 馃殌

All 2 comments

Dagger should be able to inherit that method

abstract modules are not subclassed by Dagger. abstract provision methods are read at compile-time and then completely ignored at runtime. Conversely, static provision methods are able to be invoked without an instance of the module.

A non-static, non-abstract provision method cannot be invoked by Dagger at runtime because there is not only no instance, but there is no subclass that can be instantiated.

Hmm, I guess that explains it.

I guess the best thing I can come up with in that case is

@Module(includes = [MyFragmentBuilderModule::class])
abstract class MyModule {
    @PerScreen
    @ContributesAndroidInjector
    abstract fun fragment(): MyFragmentImpl

    @Binds
    @IntoMap
    @ClassKey(MyScreen::class)
    abstract fun viewModel(viewModel: MyViewModel): ViewModel
}

@Module
internal object MyFragmentBuilderModule {
    @Provides
    @IntoMap
    @ClassKey(MyScreen::class)
    @JvmStatic
    fun fragmentBuilder(): FragmentBuilder<*, *> = MyFragmentImpl.builder()
}

Not ideal, would have preferred to make the definitions in one module, but I'll have to accept it for now, then.

The plan is to kill these Impl.builder()s anyway.

Thanks for the quick answer, by the way. 馃殌

Was this page helpful?
0 / 5 - 0 ratings