简体   繁体   中英

Why does 'get' method of class 'Map' enable sending a not relevant key without a compilation error?

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:

  1. 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).
  2. If you omit explicit generic parameter , passing as a key :
    1. an instance of K type (or its subtype) - the first overload will be called
    2. 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. 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) 

Why two get methods?

  1. Kotlin wants to inherit Java type system.
val x: kotlin.collections.List<String>= java.util.ArrayList<>()
  1. Kotlin type system is different. One of the differences is declaration-site variance

Having

open class A
class B

It is illegal in Java

List<A> x = new ArrayList<B>();

But absolutely normal in Kotlin

val x: List<A> = ArrayList<B>()

As of the Map example I would expect

Error: 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

whenever type inference is broken since Kotlin 1.0.0

If it's really a problem, it would be great to see a code paste and kotlin compiler version you are using.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM