1

I want to run a block of code depending if the variable is null or not, but I still don't know 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 option and why? Thanks :)

PS I already read How do I run a block of code if a nullable type is null? but I still don't know what is the best practice

ladytoky0
  • 588
  • 6
  • 16
  • 1
    I dont understand your question, the question you linked explains how to do what you want – tyczj Nov 09 '21 at 14:37
  • it does not have the right answer because you can not use let for some reason I don't know – ladytoky0 Nov 09 '21 at 14:38
  • how do you know it does not have the right answer if you dont know if/why its wrong, please explain. Also with what you are trying to do you have basically an if/else but much more confusing and less readable – tyczj Nov 09 '21 at 14:44
  • it is said in the comments :) I know I could use an if/else, but I'm trying to use all the kotlin properties – ladytoky0 Nov 09 '21 at 14:48

1 Answers1

4

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.

Tenfour04
  • 83,111
  • 11
  • 94
  • 154