Home > Software design >  When to use let/run/apply/with/also when an object is null?
When to use let/run/apply/with/also when an object is null?

Time:11-10

So I read How to run code if object is null? but I don't know yet when to use let,run...etc. I want something like this:

myVar?.xxx{
  // do something if is not null
} ?: xxx{
  // do something is myVar is null
}

What is the best practice? Thanks :)

CodePudding user response:

The best practice is to not chain scope functions with an Elvis operator to handle null/not null, because it is less readable and it is error-prone. Just because you can do something doesn't mean you should! Inline scope functions provide a lot of power, and therefore also a lot of potential for misuse and less readable code.

For example, the return value of the first scope function (if it is let or run) could potentially be null and cause the second scope function to also be run and return something else than you expected.

The question you linked is old, and from a time when there were a lot of new Kotlin users because of Android adopting it as a recommended language. A lot of new users excited about inline scope functions, and probably overly eager to use them as much as possible instead of just where they're the most appropriate tool. Note that in the top-voted answer that shows how it can be done, the advice is still to use if/else because that is more readable.

If myVar is a local variable (or a locally defined val property with no custom getter), you should do this:

if (myVar != null) {
    // myVar will be smart cast to non-null inside this if block.
    //TODO
else {
    //TODO
}

If myVar is a var property you can do:

myVar.let { 
    if (it != null) {
        // it will be smart cast to non-null inside this if block.
        //TODO
    else {
        //TODO
    }
}

// OR

val myLocalVar = myVar
if (myLocalVar != null) {
    // myLocalVar will be smart cast to non-null inside this if block.
    //TODO
else {
    //TODO
}

To get at you specific question, if you were really determined to do this (and you shouldn't!), it would be safest to use myVar.also { } ?: run { }. The also function doesn't return a result, but rather returns what it was called on, so there's no risk of accidentally triggering both scope functions. apply would also work, but typically if you have a whole block of code to run, you don't want to be using it as a receiver. That would be less readable. And for the second scope function, run makes the most sense because it will not change the receiver from the outer scope, so it creates the least friction to readability here.

  • Related