繁体   English   中英

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

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

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

我有以下变量:

gamesPerCountriesMap: MutableMap<Long, MutableMap<Long, MutableList<AllScoresGameObj>>>?

我有以下代码行:

var gamesList = gamesPerCountriesMap?.get(countryItem.id)?.get(competitionItem)

正确的行应该是:

var gamesList = gamesPerCountriesMap?.get(countryItem.id)?.get(competitionItem.id)

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

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

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

Map接口有两种get方法。

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

Map<K, V> { fun get(key: K): V? }

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

fun <K, V> Map<out K, V>.get(key: K): V?

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

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

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

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

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

val v = mapOf<Derived, String>().get(k as Derived) 

为什么有两个get方法?

  1. Kotlin 想要继承Java 类型系统。
val x: kotlin.collections.List<String>= java.util.ArrayList<>()
  1. Kotlin 类型系统是不同的。 差异之一是声明站点差异

拥有

open class A
class B

在 Java 中是非法的

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

但在 Kotlin 中绝对正常

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

至于我期望的Map示例

错误:类型推断失败。 类型参数 K 的值应在输入类型(参数类型、接收器类型或预期类型)中提及。 尝试明确指定它

自 Kotlin 1.0.0 起类型推断被破坏时

如果真的有问题,很高兴看到您正在使用的代码粘贴和 kotlin 编译器版本。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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