Reputation: 50989
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
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
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
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
}
list
is null, the entire let
expression will evaluate to null
, and the right side of the Elvis operator (false
) will be returned.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