```var nullTest:Int?=null nullTest?println("1"):pr...
# announcements
a
Copy code
var nullTest:Int?=null
nullTest?println("1"):println("0")
r
if (nullTest != null) println("1") else println("0")
1
c
The elvis operator “unwraps” a nullable value, it is not equivalent to the ternary operator (which intentionally does not exist in Kotlin). It’s used to get the non-null value of a variable, or else a default if it is null
Copy code
var nullTest: Int? = null
println("${nullTest ?: 0}")
If you don’t actually want the value of the variable itself, use the
if
expression Ruckus posted
p
I think you want the controversial
Copy code
nullTest?.let {
    println("1")
} ?: run {
    println("0")
}
😡 1
🤢 2
👎 4
Despite many people don't liking it, I think is the closest alternative to
if let
in swift. For this simple usage it is overkilling. But if you want to extract a non-null and operate on it, or else run some code. I haven't found anything better than bellow.
Copy code
nullTest?.let { nullTestNonNull ->
    nullTestNonNull.safeAccess()
} ?: run {
    println("nullTest is null, do something else")
}
a
println(if (nullTest != null) 1 else 0)
the reason people don't like
?.let {} ?: run {}
is because it does not behave like the if/else people try to use this as
if the expression inside the
let {}
evaluates to null the second
run
branch will execute
you'll say, "well this function I'm calling here is non-nullable, that can't happen!" and then you'll get into the habit, and you will experience this bug in your codebase at some point
that it's visually hideous as a pattern is a feature since it discourages this usage unless that double-evaluation in case of nulls is actually what you want
p
that's a good catch that I haven't thought of yet!
a
🙂
p
Thanks! I feel like we should copy
guard
and
if let
from swift.
👍 1
But I guess the language designers did not like it.
y
Actually, if anything in the expression before the
?:
gets null, then the
run { }
is called
2
💣 2
So we can really use it as if not null/else
a
you cannot
y
Just tested in Kotlin online with the code
Copy code
fun main() {
    val x: Int? = null
    x?.let { print("x is not null") } ?: run { print("x is null") }
}
and prints
x is null
p
I asked that question before and the other alternative is using
when
but in my opinion is extremely verbose for such a simple thing
a
if/else's behavior is mutually exclusive, if the first branch is taken the second one never will be. The let/?:/run thing is not mutually exclusive
✔️ 2
p
This is the real danger Adam is talking about:
Copy code
fun main() {
    
    val x: Int? = 1
    x?.let { 
        print("x is not null")
        null
    } ?: run { print("x is null") }
    
}
👆 1
If your let lambda returns
null
accidentally. Both branches execute.
✔️ 1
y
In this case I agree with you guys. But, if u just want, for example, check something like
Copy code
myList?.takeIf { it.isNotEmpty() }?.forEach {
    // do some stuff
} ?: run { 
    // otherwise
}
I think it works as expected from a if/else
a
nope, because whether it works as "expected" is entirely dependent on the specific last line of
// do some stuff
4
well, not in this case because you used
forEach
- which kind of illustrates the point - it's very much not an if/else 🙂
1
which isn't to say it's not useful, it is!
1
y
exactly! in case I used
let
instead of
forEach
then the
let
result would matter for the run block to be executed
Thanks for this information!
s
if(nullTest!=null) println(“1”) else println(“0")
p
is it too crazy?
Copy code
public inline fun <T, R> T?.whenNonNullOrElse(
    nonNullBlock: (T) -> R,
    nullBlock: (() -> R)
): R? {
    
    return if (this != null) { nonNullBlock(this) } else { nullBlock()}
    
}
Then
Copy code
fun main() {
    
    val x: Int? = null
    
    val y: Int? = x.whenNonNullOrElse(
        { 
            println("x is not null")
            null
        },
        {  
            println("x is null")
            -1
        }
    )
    
    print("y = $y")
    
}
r
But... why? What does that give you over a simple
if
? (Besides a massive reduction in readability of course.)
2
Copy code
fun main() {
    
    val x: Int? = null
    
    val y: Int? = if (x != null) { 
        println("x is not null")
        null
    } else {  
        println("x is null")
        -1
    }
    
    print("y = $y")
    
}
y
if u want to use the value like
Copy code
if(x != null) {
     x.method()
} else {
     print("null")
}
Then u have to tell somehow to the compiler that x is not null, because it still thinks that x may have changed.
p
It allows to thread safe extract the receiver
y
Thats why methods like
fun String.isNullOrBlank()
exists and use compiler contracts (and dont need
?
safe call operator)
r
That's a lot of mental overhead to avoid
val _x = x
Those kinds of methods make sense if you're just returning a Boolean, not for much more.
p
The problem with
val _x = x
it start polluting the code
r
Not any more than
x.whenNullOrElse({ ... }, { ... })
(Granted,
_x
should be replaced with a better name, but since when do we get names right? 🧌)
p
Even though
x.whenNullOrElse({ ... }, { ... })
is longer and more verbose its still a one liner shot. You can concatenated with stuff like
apply
and
also
Instead
val x = x
force you to write more than one line.
y
Like Adam said up there, just use
x?.let { } ?: run { }
and take care of what you return inside
let
, and it will kinda work like a if not null/else
That way the compiler grants you that
it
is not null inside the
let
block.
r
Personally, I see the unquenchable need to make everything in Kotlin into a one-liner an anti-pattern, but maybe I'm crazy.
1
p
@Yago Lasse I kind of buy that but remember the purpose of languages and compilers is avoid any possible human mistakes.
r
Sure, but what mistake are you avoiding?
p
The one Adam mentioned about
x?.let { null } ?: run {}
y
Agree, but there is one point that I am questioning: If we dont need the value, then we should do:
print(if(x != null) 1 else 0)
. But if we need the non null value, how to make it without a
!!
? Shadowing a variable is really the best option here?
r
That is equally solved by declaring another variable. There's no advantage other than it's "one line".
p
I think the purpose of functional paradigm is using
if/else
the least possible
🚫 1
r
Forgive my being blunt, but what does that have to do with anything? The question was about a replacement for a falsey ternary operation.
p
That's the answer! You shouldn't use
if/else
🙂. When you have a functional language that let you do cleaner stuff.
r
But Kotlin isn't a functional language, and my whole point is it isn't cleaner.
2
p
Well, the reality Kotlin is a mix of functional and OO, although the tendency I think would be to make it more functionalish. What I mean by cleaner, is removing what is known by
branch creation
in code.
if/else
are a good contributors to it, when not used as expression.
Copy code
val xLocal = x
val yLocal = y

val z = if (xLocal != null) {
    if (yLocal != null) {
        (xLocal + yLocal)*(xLocal + yLocal)
    } else {
        xLocal*xLocal + 0
    }
} else {
    if (yLocal != null) {
        0 + yLocal*yLocal
    } else {
        0 + 0
    }
}
vs
Copy code
val z = x.whenNonNullOrElse(
    { it }
    , { 0 }
) + y.whenNonNullOrElse(
    { it }
    , { 0 }
).map{
    it*it
}
k
if
is completely functional, it doesn't have state or something like that.
vs
Copy code
val z = (xLocal ?: 0) + (yLocal ?: 0)
p
Thats true when used as expression but if it start nesting it becomes bad to read.
r
@Pablichjenkov You never removed the if, you just moved it to another function call. How does that reduce branch creation?
k
I don't believe there are any cases where
whenNotNullOrElse
is cleaner than some other alternative.
p
@Ruckus That sample I posted is not good to show a branch creation issue because it is used as expression and the compiler will enforce you to exhaust all possible combinations. Now when not use as expression you may miss to declare one of the branches:
Copy code
var z = 0

 if (xLocal != null) {
    if (yLocal != null) {
        z = xLocal + yLocal
    } ...//missing branch, no compiling error but Business Logic error(worst)
} else {
    if (yLocal != null) {
        z = 0 + yLocal
    } else {
        z = 0 + 0
    }
}
@karelpeeters I agree that in many cases using the
if/else
expressiveness and the elvis operator will reduce verbosity. However, for the case when we want to apply an operation on a nullable receiver or else doSomething different, it makes it very concise to my liking. But well I agree with Ruckus I kind of diverge from the initial use case.