Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[KSP] Multibinding example from developer docs does not compile under Kotlin + KSP #4347

Open
jpicklyk opened this issue Jul 5, 2024 · 1 comment

Comments

@jpicklyk
Copy link

jpicklyk commented Jul 5, 2024

Reference: https://dagger.dev/dev-guide/multibindings.html

Kotlin - 2.0
KSP - 2.0.0-1.0.21
Hilt - 2.51.1

In an attempt to troubleshoot my multi-binding issues within my application, I am attempting to get a very simplistic example working from the Dagger documentation however I can not figure out how to get it to compile correctly. The usage of @JvmSuppressWildcards doesn't seem to fix the error:

[Dagger/MissingBinding] java.util.Map<kotlin.reflect.KClass<? extends java.lang.Number>,java.lang.String> cannot be provided without an @Provides-annotated method.

The test code:

enum class MyEnum {
    ABC, DEF
}

@MapKey
annotation class MyEnumKey(val value: MyEnum)

@MapKey
annotation class MyNumberClassKey(val value: KClass<out Number>)

@Module
@InstallIn(SingletonComponent::class)
object MyModule {
    @Provides
    @IntoMap
    @MyEnumKey(MyEnum.ABC)
    fun provideABCValue(): String {
        return "value for ABC"
    }

    @Provides
    @IntoMap
    @MyNumberClassKey(BigDecimal::class)
    fun provideBigDecimalValue(): String {
        return "value for BigDecimal"
    }

}
// @JvmSuppressWildcards in different locations within this interface makes no difference
@Component(modules = [MyModule::class])
interface MyComponent {
    fun myEnumStringMap() : Map<MyEnum, String>
    fun stringsByNumberClassMap() : @JvmSuppressWildcards Map<KClass<out Number>, String>
}

@HiltAndroidTest
@RunWith(AndroidJUnit4::class)
class HiltIntoMapInjectionTest {
    @get:Rule
    var hiltRule = HiltAndroidRule(this)

    @Before
    fun init() {
        hiltRule.inject()
    }


    @Test
    fun testIntoMapInjection() {
        val myComponent = DaggerMyComponent.create()
        assertTrue(myComponent.myEnumStringMap()[MyEnum.ABC] =="value for ABC") //Works without any @JvmSuppressWildcards annotation
        assertTrue(myComponent.stringsByNumberClassMap()[BigDecimal::class] =="value for BigDecimal") //Needs @JvmSuppressWildcards annotation but that doesn't work
    }
}
@bcorso
Copy link

bcorso commented Jul 12, 2024

When accessing the map you will need to use Class instead of KClass:

@Component(modules = [MyModule::class])
interface MyComponent {
    fun myEnumStringMap() : Map<MyEnum, String>
    fun stringsByNumberClassMap() : Map<Class<out Number>, String>
}

This is for backwards compatibility with KAPT, which will generate a Java stub for your map key like below:

@MapKey
@interface MyNumberClassKey {
    Class<? extends Number> value();
}

I think we could add a better error message for users in this case though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants