There are two get
methods for Map
interface.
One is defined directly in the body of the interface:
Map<K, V> { fun get(key: K): V? }
Another (which you cite in your question) - as an extention function:
fun <K, V> Map<out K, V>.get(key: K): V?
The overload resolution on call depends on whether or not you explicitly specify generic parameters, and on relationship between type of map K
generic parameter & type of passed key
argument:
- If you specify explicit generic parameter, the second version will be called (and it wouldn't have compiled in your case, if you've wrote
.get()<Long, MutableList<AllScoresGameObj>.(competitionItem)
, although .get()<CompetitionObj, MutableList<AllScoresGameObj>.(competitionItem)
) would've worked, cause there is unsafe cast inside this get
overload).
- If you omit explicit generic parameter, passing as a
key
:
- an instance of
K
type (or its subtype) - the first overload will be called
- anything else - the second overload (cause the first overload will not compile). In this case Kotlin will try to infer omitted generic parameters, so that original map could be represented as a
Map<out inferredK, V>
and inferredK
type parameter was a supertype of passed key
argument. Eventually, it will come up with inferredK
= Any
. Indeed Any
is a supertype of everything, and it's perfectly legal to do val x: Map<out Any, V> = mapOf<K, V>()
for any K
. Actually compiler realizes that this is a trivial solution and issues a compilation warning Type inference failed. The value of the type parameter K should be mentioned in input types (argument types, receiver type or expected type). Try to specify it explicitly.
(I believe in your case this warning should've been too). Why this still works in runtime? Because of type erasure.
So, why this overloaded version was added to stdlib?
Don't know for sure, maybe for some legal cases like:
val k : Base = Derived()
val v = mapOf<Derived, String>().get(k) // will call overloaded variant; will infer K as Base
without this overload you'd have to manually cast back:
val v = mapOf<Derived, String>().get(k as Derived)