Collections 具有非空值的Kotlin映射

Collections 具有非空值的Kotlin映射,collections,kotlin,Collections,Kotlin,假设我有一张地图,可以将扑克牌的字母转换成整数 val rank = mapOf("J" to 11, "Q" to 12, "K" to 13, "A" to 14) 在使用映射时,似乎我总是必须进行空安全检查,即使映射和映射对是不可变的: val difference = rank["Q"]!! - rank["K"]!! 我猜这是因为泛型类型有什么?超级类型。当Map和Pair都是不可变的时,为什么不能在编译时解决这个问题呢?这与Map的实现无关(无论是基于Kotlin还是基于Jav

假设我有一张地图,可以将扑克牌的字母转换成整数

 val rank = mapOf("J" to 11, "Q" to 12, "K" to 13, "A" to 14)
在使用映射时,似乎我总是必须进行空安全检查,即使映射和映射对是不可变的:

val difference = rank["Q"]!! - rank["K"]!!

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

这与Map的实现无关(无论是基于Kotlin还是基于Java)。您正在使用一个映射,而映射可能没有键,因此[]运算符返回可为null的类型。

mapOf()提供的映射不保证键的存在——这是一种预期的情况,特别是考虑到映射的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
    }
}
类非空映射(私有val映射:映射):按映射映射{
覆盖操作员乐趣获取(键:K):V{
return map[key]!!//如果密钥不存在,则强制执行NPE
}
}
通过委托给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))
funmain(args:Array){
val秩=非空映射函数(“J”到11,“Q”到12,“K”到13,“A”到14)
val jackValue:Int=rank[“J”]//按预期工作
println(jackValue)
val paladinValue:Int=rank[“P”]//如果找不到NPE,则抛出NPE,但链式调用被认为是“安全的”
println(jackValue)
}
//为创建非NullMap提供与mapOf()为Map提供的接口相同的接口
fun nonNullMapOf(vararg pairs:Pair)=NonNullMap(mapOf(*pairs))

还有另一种方法可以获取:

fun Map.getValue(键:K):V
抛出NoSuchElementException-当映射不包含指定键的值并且没有为该映射提供隐式默认值时

但是get==map[]的运算符

操作员乐趣地图。获取(键:K):V?

简单的回答是,在科特林改变之前,你无法实现这一目标。正如其他人指出的,这与可变性无关,而是Java映射接受null作为有效值的事实。目前,Kotlin的
*Map
类与Java的
*Map
类具有完全相同的实现

如果您仍然希望实现非空值纯映射,则需要实现自己的映射,例如扩展
map
或环绕它

更具体地说,在幕后,
mapOf
为我们提供了Kotlin的LinkedHashMap,它不是一个不同的类,只是Java的LinkedHashMap的一个类型别名

Maps.kt

public-fun-mapOf(vararg-pairs:Pair):映射=
if(pairs.size>0)pairs.toMap(LinkedHashMap(mapCapacity(pairs.size)))else emptyMap()
typealias.kt

@SinceKotlin(“1.1”)公共实际类型别名LinkedHashMap=java.util.LinkedHashMap
你可以尝试
map.getValue(key)
而不是
map.get(key)
,但我个人认为这是不干净和混乱的

也许丹·卢的其他人会对你有用


我的Kotlin版本当然是
1.3.72-release-IJ2020.1-3

!如果映射中不存在密钥,则返回值为空。谢谢!在我发现您的答案之前,我正要问同样的问题。对于更干净的代码,使用
值的枚举可能更可取。下面是一个快速语法示例:
fun <K, V> Map<K, V>.getValue(key: K): V
operator fun <K, V> Map<out K, V>.get(key: K): V?