StackOverflower
StackOverflower

Reputation: 5761

Why does this code is not valid in Kotlin?

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

Answers (2)

matt freake
matt freake

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

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

Related Questions