Dictionary 为什么';获取';类的方法';地图';是否启用在没有编译错误的情况下发送不相关的密钥?

Dictionary 为什么';获取';类的方法';地图';是否启用在没有编译错误的情况下发送不相关的密钥?,dictionary,kotlin,compiler-errors,Dictionary,Kotlin,Compiler Errors,我的程序中有一个bug,但我根本不明白程序是如何编译的 我有以下变量: gamesPerCountriesMap: MutableMap<Long, MutableMap<Long, MutableList<AllScoresGameObj>>>? 正确的行应该是: var gamesList = gamesPerCountriesMap?.get(countryItem.id)?.get(competitionItem.id) 我已经查看了Map类的原型

我的程序中有一个bug,但我根本不明白程序是如何编译的

我有以下变量:

gamesPerCountriesMap: MutableMap<Long, MutableMap<Long, MutableList<AllScoresGameObj>>>?
正确的行应该是:

var gamesList = gamesPerCountriesMap?.get(countryItem.id)?.get(competitionItem.id)
我已经查看了Map类的原型,方法声明如下:

公共内联操作员乐趣图。获取(键:K):V

正如我们所看到的,它可以得到K和它的子类型,但是competitionItem是类CompetitionObj的一个实例,它并没有继承长类。
那么为什么编译器没有阻止这个错误呢?我解决了这个问题,但我很清楚是什么没有阻止代码被编译?

对于
Map
接口,有两种
get
方法

直接在接口主体中定义:

Map{fun get(key:K):V}
(您在问题中引用)-作为扩展函数:

fun Map.get(key:K):V?
调用时的重载解析取决于是否显式指定泛型参数,以及映射类型
K
泛型参数与传递的
键类型
参数之间的关系:


  • 如果指定显式泛型参数,将调用第二个版本(如果您编写了
    .get()为什么有两个
    get
    方法,则在您的情况下不会编译第二个版本

  • Kotlin希望继承Java类型的系统
  • 这在Java中是非法的

    List x=new ArrayList();
    
    但在科特林绝对正常

    val x: List<A> = ArrayList<B>()
    
    val x:List=ArrayList()
    
    对于
    Map
    示例,我希望

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

    自Kotlin 1.0.0以来,每当类型推断中断时


    如果这真的是一个问题,那么看到您正在使用的代码粘贴和kotlin编译器版本就太好了。

    这确实很奇怪。我对它进行了研究,发现如果您为一个泛型类型的类编写一个扩展函数,该类在声明站点是不变的,但在函数站点是协变的,那么编译器将无法正确运行y强制执行类型。
    Map.get
    ,这与发生在函数站点的
    out K
    定义的类型是一样的。可能这实际上是一个编译器错误?@Tenfour04,我读了你的代码。我永远也不会想到复制错误。谢谢。@Tenfour04,在扩展中声明out有什么原因/好处吗在函数站点上?如果在这种情况下写或不写out有什么区别吗?我不需要在这里解释,因为我对变量主语非常了解,我无法理解。在我看来,它应该和不变性没有什么区别,因为
    K
    是一个参数,而不是返回值。结果是它是becomes“一切正常”。编译器甚至允许您传递任意匿名对象。
    mapOf().get(object{})
    @Tenfour04,如果有返回类型,这有关系吗?返回K的子类型没关系。不是吗?你的观点2.2是对的。我不敢相信我错过了。你可以将
    映射
    转换为
    映射
    ,而不会出现问题。这使得转换版本不可能
    将任何内容放入其中。因此编译器不允许出现错误让您传递任何类型的对象作为第二个重载的键。谜题的剩余部分是为什么会存在此重载。您的一个可能原因示例是故意规避泛型。如果已知
    k
    仅为基数,则应禁止使用它来
    获取
    ,除非您显式地将其强制转换为to派生。否则,映射接口根本不应该使用键类型,因为它不强制执行有关键类型的任何内容。这就像Java 4一样,没有泛型,所以您总是必须强制转换从集合中提取的对象。>如果已知k是
    ,则应该禁止使用它来获取,除非您解释citly将其强制转换为
    派生的
    为什么不呢?在最坏的情况下(如果您作为
    传递了不合适的内容,并且错过了编译器警告)您将只得到
    null
    。由于
    get
    的返回类型是
    V?
    ,因此无论如何都应该处理这种情况。这种类型严格性的轻微放松与定义不带
    K
    参数的
    Map
    接口不同。此接口中还有其他方法可以保持类型严格性(例如,
    put
    )应该禁止它,因为您没有理由期望映射会为无效键返回某些内容,因此您永远不应该使用无效键调用它。它显示警告,但也可能是错误。为什么要创建
    get()的重载
    除了允许您错误地传递无效密钥之外,没有其他用途?不幸的是,我看不到将此特定警告转换为错误的方法,但可以选择
    val x: kotlin.collections.List<String>= java.util.ArrayList<>()
    
    val x: List<A> = ArrayList<B>()