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 可以警告或拒绝编译(这很好),但它不会(目前)。
之所以没有第一眼看上去那么糟糕,是因为您不能 put
将 Int?
变成 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]
为什么这段代码可以无错编译执行?
val map = HashMap<Int, Long>()
val key :Int? = null
map.remove(key)
在 MutableMap remove 中声明为只接受不可为 null 的键,所以它甚至不应该编译。是 Kotlin 类型推断错误还是我遗漏了什么?
public fun remove(key: K): V?
Kotlin 可以警告或拒绝编译(这很好),但它不会(目前)。
之所以没有第一眼看上去那么糟糕,是因为您不能 put
将 Int?
变成 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]