具有非空值的Kotlin Map

Tom*_*son 10 collections kotlin

假设我有一张Map用于将扑克牌的字母翻译成整数

 val rank = mapOf("J" to 11, "Q" to 12, "K" to 13, "A" to 14)
Run Code Online (Sandbox Code Playgroud)

使用地图时,即使Map和Pair是不可变的,我也总是必须进行空安全检查:

val difference = rank["Q"]!! - rank["K"]!!
Run Code Online (Sandbox Code Playgroud)

我想这是因为泛型类型有Any吗?超类型.当Map和Pair都是不可变的时,为什么不能在编译时解决这个问题呢?

Nur*_*lan 19

还有另一种从 map获取非空值的方法:

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

throws NoSuchElementException - 当映射不包含指定键的值并且没有为该映射提供隐式默认值时。

但是get == map[] 的 运算符返回 nullable

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


Raf*_* G. 12

它不是关于Map的实现(基于Kotlin或Java).您正在使用Map,而地图可能没有键,因此[]运算符返回可空类型.


mbS*_*ola 8

mapOf()提供的Map不能保证键的存在 - 特别是考虑到Map的Java实现,这是一种预期的东西.

虽然我个人可能更喜欢坚持使用零安全呼叫和elvis运营商,但听起来你更喜欢在呼叫站点使用更干净的代码(特别是考虑到你知道这些密钥存在且具有相关联的非空值).考虑一下:

class NonNullMap<K, V>(private val map: Map<K, V>) : Map<K, V> by map {
    override operator fun get(key: K): V {
        return map[key]!! // Force an NPE if the key doesn't exist
    }
}
Run Code Online (Sandbox Code Playgroud)

通过委托map的实现,但是重写get方法,我们可以保证返回值是非null的.这意味着您不再需要担心!!,?.或?:用于您的用例.

一些简单的测试代码表明这是真的:

fun main(args: Array<String>) { 
    val rank = nonNullMapOf("J" to 11, "Q" to 12, "K" to 13, "A" to 14)
    val jackValue: Int = rank["J"] // Works as expected
    println(jackValue)
    val paladinValue: Int = rank["P"] // Throws an NPE if it's not found, but chained calls are considered "safe"
    println(jackValue)
}

// Provides the same interface for creating a NonNullMap as mapOf() does for Map
fun <K, V> nonNullMapOf(vararg pairs: Pair<K, V>) = NonNullMap(mapOf<K, V>(*pairs))
Run Code Online (Sandbox Code Playgroud)