Reputation: 5761
I started to learn Kotlin, I'm writing my first lines of code in this language.
I was surprised that this does not compile
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"
}
}
}
I saw a report saying that this is a compiler bug... but I'm surprised it existed unsolved for so long so I might be misunderstanding something.
I had to do
sockets[socket] = sockets[socket]!!.inc();
What it is horrible and super verbose.
I come from c# world and I do [XXX]++ all the time!! What's wrong with that in Kotlin?
Upvotes: 0
Views: 174
Reputation: 5090
The problem is that the get
that sockets[socket]
is, has a return type of V?
; it can return null if there is no value in the map for socket
. Even though you have the if (sockets.containsKey(socket))
there the compiler does not know that (for instance, if sockets
was shared, what's to stop another thread setting it to null after the if
). So what happen when you call ++
on your null?
One alternative to your verbose one is:
sockets.merge(socket, 1, Int::plus)
Upvotes: 3
Reputation: 7882
In general, Obj[XXX]++
idiom is not prohibited in Kotlin.
The problem here is that result of sockets[socket]
call has nullable type (Int?
) and postfix increment expression works only for non-nullable types (because under the hood it makes .inc()
call).
Look at this hypothetical example:
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)
}
In your case, it's better to refactor loop body to:
for (socket in ar) {
sockets.computeIfPresent(socket) { _, v -> v + 1 }
}
Upvotes: 3