[KSP] Multibinding example from developer docs does not compile under Kotlin + KSP
Opened this issue · 1 comments
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
}
}
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.