Dims
Dims

Reputation: 50989

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.

Upvotes: 4

Views: 11876

Answers (3)

anwar alam
anwar alam

Reputation: 602

I had the same problem in the given lines

 sliderView.setSliderAdapter(adapter!!)
 sliderView.setIndicatorAnimation(IndicatorAnimationType.WORM)

Finally, error resolved by adding !!

 sliderView!!.setSliderAdapter(adapter!!)
 sliderView!!.setIndicatorAnimation(IndicatorAnimationType.WORM)

Upvotes: 2

Todd
Todd

Reputation: 31660

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!

Upvotes: 6

zsmb13
zsmb13

Reputation: 89528

You can perform the null check, and if it succeeds, access a read-only copy of your variable with let:

fun isScalar() : Boolean {
    return list?.let { it.size == 1 } ?: false
}
  • If list is null, the entire let expression will evaluate to null, and the right side of the Elvis operator (false) will be returned.
  • If list is not null, then the let function is called, and result of the it.size == 1 expression is returned - it refers to the object that let was called on (list in this case). Since it's used with a safe call, this it will have a non-nullable type and size can be called on it.

Upvotes: 4

Related Questions