Reputation: 1252
I am replacing all the null checks with let block in my code as part of a code review process.
1. Code example with null check:
if(someValue != null){//run if someValue is not null}
else {//run if someValue is null}
2. Code base after using let-run block if for null check,
var someValue : String? = null
someValue = "SOF"
someValue?.let {safeSomeValue->
//This code block will run only if someValue is not null
}?.run {
//This code block should run only when if someValue is null, like else condition
}
Now problem with let-run block is that both the code blocks are running even if the someValue is not null. So i am not able to replicate behaviour of if-else condition in code example 1 to run-let condition in code example 2.
Expected behaviour is to execute either let or run code block based upon if value is null or not-null.
Upvotes: 34
Views: 19780
Reputation: 2987
Sometimes you will find the following useful
inline fun <T> T?.itOrNull(
ifValue: (T) -> Unit,
ifNull: () -> Unit
): Unit = when (this) {
null -> ifNull()
else -> ifValue(this)
}
usage:
data.title?.itOrNull(
{ view.text = it },
{ view.visibility = View.GONE }
)
Upvotes: 1
Reputation: 1252
Source - kotlinlang.org
Change ?. with ?: will solve this issue,
Code base as following, will run either let or run block based upon the null check.
var someValue : String? = null
someValue = "SOF"
someValue?.also {safeSomeValue->
//This code block will run only if someValue is not null
}?:run {
//This code block will run only if someValue is null, like else condition
}
Upvotes: 44
Reputation: 33
You can write two extension functions, eg: "ifSafe" and "ifNull", that can be used individually or chained together to mimic the if/else pattern:
fun <T> T?.ifSafe(block: (t:T?) -> Unit) = if (this!=null) block(this) else this
fun Any?.ifNull(block: () -> Unit?) = if (this==null) block().also{return null} else this
fun main() {
var someValue = 4
var nullValue = null
someValue.ifSafe { safeSomeValue ->
println("someValue: "+safeSomeValue.toString())
null
}.ifNull{
println("someValue is null.")
}
someValue.ifNull{
println("someValue is null.")
}.ifSafe{ safeSomeValue ->
println(safeSomeValue)
}
nullValue.ifNull{
println("nullValue is null.")
}
}
Upvotes: 0
Reputation: 170735
To give a very concrete example of what zsmb13's answer talks about:
val someValue = 0
someValue?.let { safeSomeValue->
println("then")
null
} ?: run {
println("else")
}
prints both then
and else
. You can fix this by using also
instead of let
:
val someValue = 0
someValue?.also { safeSomeValue->
println("then")
null
} ?: run {
println("else")
}
will only print then
. It may be useful to read https://kotlinlang.org/docs/reference/scope-functions.html and figure out why, and to prove it's indeed always equivalent to the original if ... else
. But I also agree with zsmb13 that it's probably a bad idea.
Upvotes: 16
Reputation: 89548
I am replacing all the null check with let block in my code
The first question here is, why? Is this somehow more readable for you than the regular if-else
structure? I'd be generally wary of refactoring just for refactoring's sake.
The second consideration is much more important: this conversion you're doing is not equivalent to the original code, you're actually modifying the behavior with this change. Take the following piece of code:
var someValue : String? = null
someValue = "SOF"
someValue?.let {safeSomeValue->
foo(someSafeValue)
bar(someSafeValue)
} ?: run {
println("shouldn't happen if someValue != null")
}
You expect the run
block to execute only if someValue == null
, but that's actually not the only case when it will run. The entire someValue?.let { ... }
expression can produce null
values not just when someValue
itself was null
, but also if the block passed to let
returned null
. In this case, if the bar()
function call results in null
, the run
statement will be executed afterwards, therefore running both branches of what you thought was a fancied up if-else
statement.
Upvotes: 16
Reputation: 29260
You can create an extension function, like this
fun <T> T?.whenNull(block: () -> Unit) = this ?: block()
then you call it like this
somevalue?.let { safeSomeValue ->
// TODO
}.whenNull {
// execute when someValue is null
}
Upvotes: 7