?.let vs if not null


#1

What is actually the difference and what is the preferred to use:

variable?.let { //... }

or

if (variable != null) { //... }


#2

Case 2 doesn’t work with mutating properties, instead in case 1 the variable it is always not null.


#3

While I use the first for the reason described by fvasco (you’d have to use varable!! in many cases), I don’t find it to be really clear for those new to the language. To address, I created a function whenNotNull(), which I can use as:

whenNotNull(variable) { }

This seems slightly more self documenting and understable from my perspective.


#4

Could you please share that funciton? There is an simple if (variable != null) statement inside? :smiley:

I just made this:

fun Any?.notNull(f: ()-> Unit){
    if (this != null){
        f()
    }
}

so we can use

var s: String? = null`

s.notNull{
    println("s is not null")
}

What about this approach in comparison to 1) and 2) ?


#5

My function is

inline fun <T:Any, R> whenNotNull(input: T?, callback: (T)->R): R? {
    return input?.let(callback)
}

The version with the receiver would also work fine; just a variation on a theme.


#6

Another option is to use an immutable copy

val copy = variable
if (copy != null) {
  // copy is guaranteed to be to non-nullable whatever you do
}

#7

I just found out the difference i was looking for, from this video: https://www.youtube.com/watch?v=0sPzDwS55wM&list=WL

When i use an if statement with a var, the var can be changed by some other thread in the middle of my if statement, whilst with ?.let {}, the var is read only once and then used inside the let closure, i suppose similarly to the copy method suggested by @Filipp.Riabchun

So as i assume all var variables in kotlin are volatile, the ?.let method is more preferred with them, while with val there is no difference between let and if


#8

Variables in Kotlin are not any more volatile than they are in Java. They certainly don’t get the volatile keyword. Kotlin does however try to be thread-safe by default though as long as you don’t break your thread safety yourself (you need to provide some sort of synchronization (volatile, synchronized, locks)).


#9

It’s not about thread safety, it’s about null-safety. Kotlin tries to provide null-safety even when the variable could be changed from another thread. If you checked your local variable and it’s not null, nothing in the world could make it null, so Kotlin compiler allows to treat is as a non-null value. If you checked your field and it’s not null, it could become null at any moment, so if you use it after check, Kotlin still can’t guarantee that NPE won’t fire, so it doesn’t allow you to use it as a non-null value.

That’s not convenient, if you ask me. In 99.99% cases fields are not shared between threads, so I would take a theoretical risk of getting NPE over a convenience (or just introduce implicit local variable automatically). There are too many cases when I’m copying field to local variable just so null-check would work.


#10

This is not about thread safety as you wrote. The field could be set to null by the current thread during any random method call or whatever.

if(field != null) {
    doSomething() // could set field to null
    field.foo() // nobody can guarantee here that field is != null
}

And automatically copying to a local variable doesn’t make sense here because it changes semantics in case you want to change the field somewhere.


#11

I took the liberty to update your code:

fun <T : Any> T?.notNull(f: (it: T) -> Unit) {
    if (this != null) f(this)
}

This way if you have something like this, it will compile:

    val someString: String? = null
    someString.notNull { doSomething(it) }

    fun doSomething(param: String) {
        // do your stuff here
    }