I just had a bug in my program, but i don't understand how the program was compiled at all!
I have the following variable:
gamesPerCountriesMap: MutableMap<Long, MutableMap<Long, MutableList<AllScoresGameObj>>>?
and i had the following line of code:
var gamesList = gamesPerCountriesMap?.get(countryItem.id)?.get(competitionItem)
the correct line should be:
var gamesList = gamesPerCountriesMap?.get(countryItem.id)?.get(competitionItem.id)
i have looked at the prototype of the Map class and the method is declared as following:
public inline operator fun <@kotlin.internal.OnlyInputTypes K, V> Map<out K, V>.get(key: K): V?
As we can see it can get K and it's subtype, but competitionItem which is an instacne of class CompetitionObj isn't inherit the Long class. So why the compiler didn't prevent this error? I solved the issue but i am very couries of what is didn't prevent the code from being compiled?
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:
.get()<Long, MutableList<AllScoresGameObj>.(competitionItem)
, although .get()<CompetitionObj, MutableList<AllScoresGameObj>.(competitionItem)
) would've worked, cause there is unsafe cast inside this get
overload).key
:
K
type (or its subtype) - the first overload will be calledMap<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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With