Kotlin 不可空映射允许删除 null

Kotlin non nullable map allows remove null

为什么这段代码可以无错编译执行?

val map = HashMap<Int, Long>()
val key :Int? = null
map.remove(key)

在 MutableMap remove 中声明为只接受不可为 null 的键,所以它甚至不应该编译。是 Kotlin 类型推断错误还是我遗漏了什么?

public fun remove(key: K): V?

Kotlin 可以警告或拒绝编译(这很好),但它不会(目前)。

之所以没有第一眼看上去那么糟糕,是因为您不能 putInt? 变成 MutableMap<Int, Long> 因为

val map = HashMap<Int, Long>()
val key :Int? = null
map.put(key, 1) // <--- WON'T COMPILE [Type mismatch: inferred type was Int? but Int was expected]
map.remove(key)

尽管如此,我认为您对正在编译的方法感到疑惑是对的。

您的代码非常好,因为 remove() 允许可空参数 - 您的地图 contents 定义没有任何内容。当 remove() 被调用时,它会尝试在地图中找到匹配的请求键,因为它不存在(它不存在的原因完全不相关 - 键不存在是有效的情况)什么都不会发生。编译器会抱怨任何尝试 put 这样的键到你的地图。然后映射定义开始,因为已知不允许为 null 的键,所以这样的代码甚至不会编译,因为这显然是有错误的代码。

最终提出这个问题有助于找到 问题和解释。简而言之,实际发生的是调用具有自己的类型推断的扩展函数。

在这种情况下,map.remove(key) 不会调用

public fun remove(key: K): V?

它调用扩展remove函数:

public inline fun <@OnlyInputTypes K, V> MutableMap<out K, V>.remove(key: K): V? =
    @Suppress("UNCHECKED_CAST") (this as MutableMap<K, V>).remove(key)

此函数文档说它 允许克服 remove 的类型安全限制,该限制需要传递 K.[= 类型的键29=]


它允许克服类型安全限制,因为您要删除的条目的键不必与您传递给 remove(key) 的对象的类型相同;该方法的规范只要求它们相等。这源于 equals() 方法如何将 Any 作为参数,而不仅仅是与对象相同的类型。

尽管许多 classes 都定义了 equals(),因此它的对象只能等于它自己的对象 class,但有很多地方不是这种情况。例如,List.equals() 的规范说如果两个 List 对象都是 List 并且具有相同的内容,那么它们是相等的,即使它们是 List 的不同实现.因此,例如,根据方法的规范,可以有一个 MutableMap<ArrayList<Something>, Something> 并使用 LinkedList 作为参数调用 remove(key),并且它应该检索是具有相同内容的列表。如果此扩展 remove(key) 不存在,这将是不可能的。[1]