为什么“Map”类的“get”方法可以在没有编译错误的情况下发送不相关的键?

Eit*_*s30 6 dictionary compiler-errors kotlin

我只是在我的程序中有一个错误,但我根本不明白该程序是如何编译的!

我有以下变量:

gamesPerCountriesMap: MutableMap<Long, MutableMap<Long, MutableList<AllScoresGameObj>>>?
Run Code Online (Sandbox Code Playgroud)

我有以下代码行:

var gamesList = gamesPerCountriesMap?.get(countryItem.id)?.get(competitionItem)
Run Code Online (Sandbox Code Playgroud)

正确的行应该是:

var gamesList = gamesPerCountriesMap?.get(countryItem.id)?.get(competitionItem.id)
Run Code Online (Sandbox Code Playgroud)

我查看了 Map 类的原型,该方法声明如下:

公共内联运算符 fun <@kotlin.internal.OnlyInputTypes K, V> Map<out K, V>.get(key: K): V?

正如我们所看到的,它可以获取 K 并且它是子类型,但是作为 CompetitionObj 类的 instacne 的竞争项并没有继承Long类。那么为什么编译器没有阻止这个错误呢?我解决了这个问题,但我很清楚什么没有阻止代码被编译?

Мих*_*аль 3

接口有两种get方法Map

一种是直接在接口主体中定义:

Map<K, V> { fun get(key: K): V? }
Run Code Online (Sandbox Code Playgroud)

另一个(您在问题中引用的)- 作为扩展函数:

fun <K, V> Map<out K, V>.get(key: K): V?
Run Code Online (Sandbox Code Playgroud)

调用时的重载决策取决于您是否显式指定泛型参数,以及映射K泛型参数类型与传递参数类型之间的关系key

  1. 如果您指定显式泛型参数,则将调用第二个版本(并且在您的情况下它不会编译,如果您编写了.get()<Long, MutableList<AllScoresGameObj>.(competitionItem),尽管.get()<CompetitionObj, MutableList<AllScoresGameObj>.(competitionItem))会起作用,因为此get重载内存在不安全的强制转换)。
  2. 如果省略显式通用参数,则作为以下形式传递key
    1. 类型(或其子类型)的实例K- 将调用第一个重载
    2. 其他任何东西 - 第二个重载(因为第一个重载将无法编译)。在这种情况下,Kotlin 将尝试推断省略的泛型参数,以便原始映射可以表示为 aMap<out inferredK, V>并且inferredK类型参数是传递参数的超类型key。最终,它会得出inferredK= Any。IndeedAny是所有事物的超类型,对val x: Map<out Any, V> = mapOf<K, V>()任何K. 实际上编译器意识到这是一个微不足道的解决方案并发出编译警告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.(我相信在你的情况下这个警告也应该是)。为什么这在运行时仍然有效?因为类型擦除

那么,为什么这个重载版本会被添加到 stdlib 中呢?不确定,也许对于一些法律案件,例如:

val k : Base = Derived()
val v = mapOf<Derived, String>().get(k) // will call overloaded variant; will infer K as Base
Run Code Online (Sandbox Code Playgroud)

如果没有这个重载,你就必须手动强制转换:

val v = mapOf<Derived, String>().get(k as Derived) 
Run Code Online (Sandbox Code Playgroud)