I'm attempting advent of code and wanted to create a class for day 10. I know that the values can be null, so I declared them as nullable. At some point, I need to check whether the value is assigned and do something with it.
There comes the issue. I check beforehand via high != null
, but in the line that follows, I have to use !!
to convince the compiler that it actually is null.
It seems that it can't find the proper compareTo
method, despite nullchecking it first. I guess, it didn't smartcast my variable
private class Bot(val number: Int, var low: Int?, var high: Int?) {
fun acceptValue(value: Int) {
if (low == null && high == null) {
high = value
} else {
if (high != null) {
if (high!! > value) { //it doesn't compile, because appareantly, high is still not considered nonnull at this point
low = value
} else {
low = high
high = value
}
}
}
}
}
the kotlin-version is use is 1.1.3-2
Is that a bug? Am I doing something wrong?
Between high != null
and high > value
another thread could do high = null
, invalidating the null check. So this is the expected behavior.
The way to solve this is to use a temporary variable which cannot be externally changed:
val cHigh = high
if (cHigh != null) {
if (cHigh > value) {
....
Since you defind high
as a var
, it is mutable. You cannot guarantee the variable is not null even if you have an explicit null check before that.
Official explanation:
Note that smart casts do not work when the compiler cannot guarantee that the variable cannot change between the check and the usage. More specifically, smart casts are applicable according to the following rules:
- val local variables - always;
- val properties - if the property is private or internal or the check is performed in the same module where the property is declared. Smart casts aren't applicable to open properties or properties that have custom getters;
- var local variables - if the variable is not modified between the check and the usage and is not captured in a lambda that modifies it;
- var properties - never (because the variable can be modified at any time by other code).
In you case, you can use .let
:
high?.let {
if (it > value) {
low = value
} else {
low = it
high = value
}
} ?: run {
//high == null
}
Suggested reading: In Kotlin, what is the idiomatic way to deal with nullable values, referencing or converting them
You can solve this issue if you declare the propertiries private
:
private class Bot(val number: Int,
private var low: Int?,
private var high: Int?) {
...
}