问题描述
我开始学习 Kotlin,我正在用这种语言编写我的第一行代码。
我很惊讶这不能编译
fun sockMerchant(n: Int,ar: Array<Int>): Int {
var sockets = HashMap<Int,Int>()
for (socket in ar)
{
if (sockets.containsKey(socket))
{
sockets[socket]++; // <= Error here "No set method providing array access"
}
}
}
我看到一个 report 说这是一个编译器错误......但我很惊讶它存在这么久没有解决,所以我可能会误解一些东西。
我必须这样做
sockets[socket] = sockets[socket]!!.inc();
这是多么可怕和超级冗长。
我来自 c# 世界,我一直在做 [XXX]++!! Kotlin 中有什么问题?
解决方法
问题是 get
所在的 sockets[socket]
的返回类型为 V?
;如果映射中没有 socket
的值,它可以返回 null。即使您在那里有 if (sockets.containsKey(socket))
,编译器也不知道(例如,如果 sockets
是共享的,在 if
之后如何停止另一个线程将其设置为 null)。那么当您在 null 上调用 ++
时会发生什么?
替代冗长的另一种方法是:
sockets.merge(socket,1,Int::plus)
,
一般来说,Obj[XXX]++
习语在 Kotlin 中是不被禁止的。
这里的问题是 sockets[socket]
调用的结果具有可空类型 (Int?
) 并且后缀增量表达式仅适用于不可空类型(因为在幕后它进行了 .inc()
调用)。
看看这个假设的例子:
class HashMapWithNonNullableGet<K,V> : HashMap<K,V>() {
override fun get(key: K) : V = super.get(key)!!
}
fun sockMerchant(n: Int,ar: Array<Int>) {
val sockets = HashMapWithNonNullableGet<Int,Int>()
for (socket in ar) {
if (sockets.containsKey(socket)) {
sockets[socket]++ // sockets[socket] returns Int,so no errors here
}
}
println(sockets)
}
在您的情况下,最好将循环体重构为:
for (socket in ar) {
sockets.computeIfPresent(socket) { _,v -> v + 1 }
}