I come from Java so I'm relatively new with Kotlin. Yesterday afternoon I was trying to understand why cannot achieve this behaviour with type generics in Kotlin:
I have the following declarations:
data class Parent(val a : Double)
data class Child(val a : Double, val b : String) : Parent(a)
... more Parent derived classes
interface Foo<T : Parent> {
fun setValue(v : T)
}
class ChildFooImpl : Foo<Child> {
override fun setValue(v : Child) {
...
}
}
... more Foo implementations with Parent derived types
Next, I need to have a map to store some Foo implementations indexed by class type:
val myStore : Map<KClass<out Parent>, Foo<out Parent>> = mapOf(
Child::class to ChildFooImpl(),
... more Pair creation
)
And here comes the problem: Why do I obtain a Type mismatch: inferred type is Child but Nothing was expected error when calling setValue() method of the retrieved item from map?
val theImpl = myStore[Child::class]
theImpl?.setValue(Child(0.0, "aaa")) // <------- The error
theImpl
has Foo<out Parent>?
type so I don't figure out why is this happening. I read the Kotlin documentation page about Generics and Type Safety but cannot understand the issue whith the type projection.
Some clear explanation would be really appreciated.
Nothing in the type of the
myStore
map guarantees any relationship between the type of the key in the map, and the type of the correspondingFoo
value.So, when you do
val theImpl = myStore[Child::class]
, the only thing you know abouttheImpl
is that it's aFoo<out Parent>
. It could be aFoo<OtherChild>
for instance, which only acceptsOtherChild
values, not any arbitrary value of other subtypes ofParent
. SotheImpl.setValue(...)
is not safe to call with anything at all (thusNothing
is expected).The usual way to deal with this would be to wrap this map into a custom type that ensures you control the values that are put in there. The custom function to insert elements would enforce that the key matches the generic type of the
Foo
value, and the custom getter function would perform an unchecked cast (that you know is safe) on the value it returns.