In Java, I'd write something like this:
if (foo != null && foo.bar()) { ...
However Kotlin complains that:
Smart cast to 'Foo' is impossible, because 'foo' is a mutable property that could have been changed by this time
I think it's saying what I wrote isn't thread-safe. However, in the context where I'm using it I know it is as this will always be invoked on a single thread.
Yes foo needs to be mutable. I realize that making foo a val would solve this but that is not possible here.
What's the correct idiom in Kotlin to handle this case?
In this case, a null-safe call returns a Boolean? so you can check if it equals true:
if (foo?.bar() == true) {
}
If you need non-null foo inside the conditional, then you can use the common ?.let idiom.
foo?.let { foo ->
if (foo.bar()) {
}
}
If you know it’s only accessed on this same thread, the !! operator would be safe after the null check, but ?.let is more idiomatic so easier to follow once you’re used to reading Kotlin.
The trick is to use Kotlin's excellent null-safety operators to avoid having to do redundant checks.
First, we use the safe-call operator.
foo?.bar()
This is a Boolean? (i.e. a nullable Boolean) which is null if foo is null, or the result of bar() if not. Now, a Boolean? is not a valid condition in an if statement, obviously, so we need to provide a "default" value of false. We do that using the amusingly-named Elvis operator
if (foo?.bar() ?: false) { ... }
If foo is null, then foo?.bar() is null, and ?: returns the value of the right-hand side, i.e. false. If foo is non-null, then foo?.bar() is the result of calling bar() on foo, and (assuming that result is also non-null), ?: returns the existing non-null Boolean value.
In case where the condition is not just a function call, e.g.
foo != null && foo.bar() > 0
you can use let or run:
if (foo.let { it != null && it.bar() > 0 }) { ... }
if (foo.run { this != null && bar() > 0 }) { ... }
Related
I have an instance which can be null. Fox example
var str: String? = null
So I need to check if str is String. Do I need to check for null if I use the is operator.
First option:
if(str is String) {}
Second option:
if(str != null && str is String) {}
Please help me which way is better to use ?
The is operator is safe and returns false in the case you supply a null instance
https://pl.kotl.in/HIECwc4Av
Somewhere, you HAVE to nullcheck.
Kotlin provides many ways to enforce non-null:
Use a non-null type:
var nonNull : String = ""
var nullable : String? = "" // notice the ?
nullable = null // works fine!
nonNull = null // compiler error
and if you encounter a nullable type, you can use let {} ?: run {} construct to unwrap it and execute your code with a non-nullable:
nullable?.let { // use "it" to access the now non-null value
print(it)
} ?: run { // else
print("I am null! Big Sad!")
}
Kotlin strictly distinguishes between nullable T? and nonnull T.
Use T wherever possible to avoid null checks.
Is it possible to make the following code to compile in Kotlin?
val variable: String? = "string"
val (a, b) = variable?.run {
1 to 2
}
The compiler does not allow destructuring because the expression on the right-hand side is typed as a nullable Pair<Int, Int>?, and it's unclear what values a and b should get in case variable is null.
To solve this, you need to get a not-null expression after =.
There's a lot of different ways to deal with nullable values and produce a not-null value from a nullable one, see: In Kotlin, what is the idiomatic way to deal with nullable values, referencing or converting them
For example, if you want to provide fallback values for a and b, then use the ?: operator as follows:
val (a, b) = variable?.run {
1 to 2
} ?: (0 to 0)
An alternative, for example, would be to check variable for null first:
val (a, b) = checkNotNull(variable) { "variable should never be null" }.run {
1 to 2
}
Null doesn't have any destructuring declarations. If you want a value of null to destructure like it's a pair of nulls, you could add these extensions:
operator fun <T> Pair<T, *>?.component1() = this?.component1()
operator fun <T> Pair<*, T>?.component2() = this?.component2()
Otherwise, as the other answer shows, you need to provide a default using the Elvis operator.
It's not automatic because it doesn't know what you want. Depending on what you're doing with it, 0 to 0 may be most appropriate, or maybe -1 to -1 or 0 to null or null to null.
The question is, what do you want to do if your variable is null? If you want to throw an exception, go with require or check as #hotkey suggested.
However I have the case where I just want to return if the value is null. So I wrote myself a little helper function that allows for this:
private inline fun <T> T?.exitIfNull(exitBlock: () -> Nothing): T {
if (this == null)
exitBlock()
else
return this
}
You can call this function as follows:
val (a, b) = variable?.run {
1 to 2
}.exitIfNull {
return
}
A nice little use of the Nothing keyword in Kotlin that I personally find quite fascinating
I have a simply function check like this:
fun parseValidBluetoothBrickedId(controllerId: String?): Boolean{
if(controllerId != null){
if(controllerId.startsWith(BLUETOOTH_NAME_PREFIX) && controllerId.length > BLUETOOTH_NAME_PREFIX.length)
return true
}
return false
}
I want to convert it to simplier style:
fun parseValidBluetoothBrickedId(controllerId: String?) =
controllerId?.length > BLUETOOTH_NAME_PREFIX.length
&& controllerId?.startsWith(BLUETOOTH_NAME_PREFIX)
but IDE(Android Studio 3.0 Beta7) gives me an error, underlines the greater than('>') operator:
Operator calls corresponds to a dot-qualified call 'controllerId?.length.compareTo(BLUETOOTH_NAME_PREFIX.length) which is not allowed here
Also it underline as an error line controllerId?.startsWith(BLUETOOTH_NAME_PREFIX) and says:
Type mismatch. Required: Boolean, Found Boolean?
What is the problem, really? It is just a simply method, works well with the first block if-else style.
You can't call compareTo (use the < operator) on controllerId?.length, since its type is Int?, which means it might be null, in which case it can't be compared as a number.
Similarly, the controllerId?.startsWith(BLUETOOTH_NAME_PREFIX) call returns Boolean? (since it will return null if controllerId is null), which can't be passed to the the && operator, only a real Boolean can.
The solution here is to do the null check that you were doing in your original method, and get rid of the safe calls by relying on smart casts casting your controllerId to String:
fun parseValidBluetoothBrickedId(controllerId: String?): Boolean =
controllerId != null
&& controllerId.length > BLUETOOTH_NAME_PREFIX.length
&& controllerId.startsWith(BLUETOOTH_NAME_PREFIX)
When you do controllerId?.length, you are getting an Int?. You cannot compare Int with an Int?. That is the reason you get the first error.
You get the other error because controllerId?.startsWith(BLUETOOTH_NAME_PREFIX) returns Boolean?. You cannot use the && operator on a nullable parameter. It requires two parameters of type Boolean.
To solve the problems, you need to check controllerId != null first. This will smart cast controllerId to the non-nullable type String. Like this:
fun parseValidBluetoothBrickedId(controllerId: String?): Boolean =
controllerId != null
&& controllerId.startsWith(BLUETOOTH_NAME_PREFIX)
&& controllerId.length > BLUETOOTH_NAME_PREFIX.length
Your transformed function is missing the null check:
fun parseValidBluetoothBrickedId(controllerId: String?) =
controllerId != null && controllerId.length > "".length
&& controllerId.startsWith("")
As in your first example, the null check is necessary for the compiler to know, that controllerId is not null. After the check, the compiler uses "smart casting" and the calls are safe.
Therefore you do not have to use ?. notation after the check.
Let f() return a nullable value.
What I want to do is that
if f() is null, get an empty list,
else if f() is not null, get a list of the single item value.
In Scala, we can do something like this:
Option(f()).toList
or more verbosely
Option(f()).map(v => List(v)).getOrElse(List.empty)
In Kotlin, there is no Option (assuming no Funktionale library), and null does not have toList() unlike (None: Option) in Scala.
We have the Elvis operator, but null will be inside the listOf() function, so it will be
listOf(f() ?: /* What can I do here? */)
What we want for null is listOf(/*no argument */), but the Elvis operator requires an argument, so listOf(f() ?: ) will result in a compile error.
At least we can do
val v = f()
if (v == null) listOf() else listOf(v)
but it is a two liner.
Is there some expression for this?
Where I will use this expression is in the class's primary constructor default argument, so if it is not a one liner, it will be enclosed in brackets, so something like this:
class A(
val p1: List<V> = run {
val v = f()
if (v == null) listOf() else listOf(v)
},
val p2: ... = ...,
...)
This looks pretty ugly, isn't it?
EDIT
As #Naetmul pointed out, listOfNotNull(f()) is syntactically better to what I originally posted below, and also takes a variable number of arguments, for example
val myList = listOfNotNull(f(), g(), h())
will return a list of all the results that were not null.
I would use let here.
val myList = f()?.let { listOf(it) } ?: emptyList()
Use a ?. safe call on the return value of f(), then use let to run a code block. If f() is null, it won't run this block of code, resulting in a null value. Then we use the ?: elvis operator to fall back to an empty list.
Here it is broken up into several lines for a better understanding
val myValue = f()
val myList: List<Any>
if (myValue != null) {
myList = listOf(myValue)
} else {
myList = emptyList()
}
For this specific question, I can do
listOfNotNull(f())
Consider a class with id field which might be null until stored in database:
class IdableK<T : IdableK<T>> : Comparable<T> {
private var id : Long? = null
}
I am trying to implement a compareTo method as follows:
override fun compareTo(other: T): Int {
if (id == null) {
return -1;
}
if (other.id == null) {
return 1;
}
return id!!.compareTo(other.id!!)
}
Is this a correct way of doing it? Would there be a simple way of doing it?
Check out the kotlin.comparisons package. e.g. You can use compareValues:
class IdableK<T : IdableK<T>> : Comparable<T> {
private var id: Long? = null
override fun compareTo(other: T) = compareValues(id, other.id)
}
This is incorrect. If you have two instances with their ids set to null, both instances will return -1 when you call compareTo(other) on them, while if one returns -1 the other should return 1 in a correct implementation. I'm not sure if there are situations where it makes sense to implement compareTo based on nullable properties, but I can't imagine any. Maybe there's a better way for you too?
Also, you should avoid non-null assertions (!!). Since you're using vars, other threads may change the value to null so that even if you did a null check before, the value is now null and !! throws. Instead, you should store both ids in local variables and check these for null values.
If you absolutely have to use compareTo, I'd do it like this:
override fun compareTo(other: T): Int {
val thisId = id
val otherId = other.id
if (thisId == null && otherId == null) return 0
if (thisId == null && otherId != null) return -1
if (thisId != null && otherId == null) return 1
// thisId and otherId are now smart cast to Long
return thisId.compareTo(otherId)
}
Here is a simple way:
override fun compareTo(other: T) :Int {
return id?.compareTo(other.id ?: return 1) ?: -1
}
However this piece of code is very unfriendly to a novice kotlin programmer. It involves too much magic that make it look like scala. These 3 question marks make people puzzled, at least they must think for a minute or two before they could realize what is going on in this minimalistic one-liner. I still prefer your edition. It's more verbose, but clear.
And I'm really worried about the symmetric problem. This matters, and isn't just a design problem. If you don't compare nullable properties, there won't be this programming puzzle. It will just be override fun compareTo(other: T) = id.compareTo(other.id). Simple, clear, and no misleading.
I would rather throw away all null checking code and just live with those null assertions. Because mostly you won't compare there things until it is fully initialized. If these assertion fails, it means something really bad has happens.
Oh, BTW, I don't know about your project, and if it hits the rare cases that you have to compare nullable properties, I think you could write a special edition of Comparator that consider nulls instead of throwing NPEs. Don't mess with the natural order.