Reputation: 2794
I'm wondering how to correctly use the in/out variance modifiers in this particular case.
I have a generic Map
with a specific implementation type and I'd like to assign it to a variable under a more generic type:
var generalMap: Map<SimpleExpression<Any>, Any> = emptyMap()
var specificMap: Map<StringExpression, String?> = makeSomeMap()
generalMap = specificMap // this assignment won't compile :(
Also, StringExpression
extends SimpleExpression<String>
. I've tried monkeying with the out
variance modifier a few different ways on the generalMap
definition:
var generalMap: Map<out SimpleExpression<Any>, out Any?> = emptyMap()
...but unfortunately ran into the same compiler error.
Upvotes: 1
Views: 667
Reputation: 10106
Such assignment is only possible if SimpleExpression<T>
only produces T value. If so, make your interface or class declaration as following:
interface SimpleExpression<out T>
The next thing you have to change it is declaration of generalMap
. Add out
variance to SimpleExpression<..>
and make Any
nullable:
var generalMap: Map<out SimpleExpression<Any>, Any?> = emptyMap()
After that compiler allows you to assign specificMap
to generalMap
safely.
In case if T
in SimpleExpression
is produced and consumed as well (has methods like fun consume(t: T)
and fun produce(): T
) then you should make your declaration of generalMap
like this:
var generalMap: Map<out SimpleExpression<*>, Any?> = emptyMap()
Upvotes: 2
Reputation: 204698
Map
is not variant on the key type, as it occurs both in in
position to .get()
and out
position from .keys()
. It's not safe to treat it as out
because
var specificMap: Map<StringExpression, String?> = makeSomeMap()
var generalMap: Map<SimpleExpression<Any>, Any> = specificMap
generalMap[intExpression] // specificMap.get(not a StringExpression)
That being said, there is nothing stopping you from making an unchecked cast if you believe this does not cause any actual problems with the specific Map
implementation returned by makeSomeMap()
.
Upvotes: 2