Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Smart cast is impossible, because ... is a mutable property that could have been changed by this time

I am trying to get a class, which combines list, set and map in Kotlin. I wished to write isScalar function, which should return true if object contains only one element and wrote

import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap
import it.unimi.dsi.fastutil.objects.ReferenceArrayList
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet

class Args {

    var list : ReferenceArrayList<M>? = null

    var set : ReferenceOpenHashSet<M>? = null

    var map : Reference2ReferenceOpenHashMap<M, M>? = null

    fun isEmpty() : Boolean {
        return list === null && set === null && map === null
    }

    fun isScalar() : Boolean {
        if(list !== null && list.size == 1) {
            return true
        }
    }

}

Unfortunately it gave me error in comparison

list !== null && list.size == 1

saying

Smart cast to 'ReferenceArrayList<M>' is impossible, because 'list' is a mutable property that could have been changed by this time

As far as I understood, this is related with multithreaded assumption. In Java I would make function synchronized if would expect multithreding. Also, I would be able to disregard this at all, if I am not writing thread-safe.

How should I write in Kotlin?

I saw this solution https://stackoverflow.com/a/44596284/258483 but it expects MT, which I don't want to. How to avoid smart casting if it can't do it?

UPDATE

The question is how to do this in the same "procedural" form. How not to use smart casting?

UPDATE 2

Summarizing, as far as I understood, it is not possible/reasonable to explicitly compare variable with null in Kotlin at all. Because once you compare it, next time yous hould compare it with null again implicitly with such operations like .? and you can't avoid this.

like image 470
Dims Avatar asked Sep 07 '25 14:09

Dims


1 Answers

If you take advantage of the fact that null cannot equal 1 (or anything else, really), you can make this check very concise:

fun isScalar() : Boolean =
    list?.size == 1

When a null-safe call to list.size returns null, we get false because 1 != null. Otherwise, a comparison of whatever value size returns is made, and that works as you would expect.

By using the null safe operator (?.) you are avoiding a smart cast entirely. Kotlin gives us smart casts to make code cleaner, and this is one of the ways it protects us from misuses of that feature. Kotlin isn't going to protect us from everything (division by zero, the example you use in comments, for example). Your code is getting caught up in a legitimate case of where smart casting can go wrong, so Kotlin jumps in to help.

However, if you are absolutely sure there are no other threads working, then yes, this check is "wrong". You wouldn't need the warning in that case. Judging by this thread on kotlinlang.org, you aren't the only one!

like image 57
Todd Avatar answered Sep 10 '25 05:09

Todd