87

Suppose we have this code:

class QuickExample {

    fun function(argument: SomeOtherClass) {
        if (argument.mutableProperty != null ) {
            doSomething(argument.mutableProperty)
        } else {
            doOtherThing()
        }
    }

    fun doSomething(argument: Object) {}

    fun doOtherThing() {}
}

class SomeOtherClass {
    var mutableProperty: Object? = null
}

Unlike in Java, where you could be left alone to worry about null dereferencing at runtime, this doesn't compile - quite rightly. Of course, mutableProperty may no longer be null once within the 'if'.

My question is what's the best way to handle this?

A few options are apparent. Without using any new Kotlin language features, the simplest way is obviously to copy the value to a method-scope one that won't subsequently change.

There's this:

fun function(argument: SomeOtherClass) {
    argument.mutableProperty?.let {
        doSomething(it)
        return
    }
    doOtherThing()
}

This has the obvious disadvantage that you need to return early or otherwise avoid executing the subsequent code - OK in certain, small contexts, but has a smell to it.

Then there's this possibility:

fun function(argument: SomeOtherClass) {
    argument.mutableProperty.let {
        when {
            it != null -> {
                doSomething(it)
            }
            else -> {
                doOtherThing()
            }
        }
    }
}

but whilst it has greater clarity of purpose, arguably it's more unwieldy and verbose than the Java-style way of dealing with this.

Am I missing anything, and is there a preferred idiom with which to achieve this?

Rob Pridham
  • 4,780
  • 1
  • 26
  • 38
  • 2
    IMO I would really think that simply `with(argument.mutableProperty) { if (this != null) a(this) else b() }` is concise enough (or the equivalent with `let`.) Usually this shouldn't be much of a concern and this is still quite short. – Salem Aug 25 '17 at 08:22
  • Looks good to me, thanks! I suggest you add it as an answer - at the very least, it's another way of achieving it. – Rob Pridham Aug 25 '17 at 08:26

8 Answers8

116

Update:

As mentioned by franta on the comments, if the method doSomething() returns null, then the code on the right side of the elvis operator will be executed, which might not be the desired case for most. But at the same time, in this case, it is very likely that the doSomething() method will only do something and not return anything.

And an alternative: as protossor has mentioned on the comments, also can be used rather than let, because also returns this object instead of the result of the function block.

mutableProperty?.also { doSomething(it) } ?: doOtherThing()

Original answer:

I would use let with Elvis operator.

mutableProperty?.let { doSomething(it) } ?: doOtherThing()

From the doc:

If the expression to the left of ?: is not null, the elvis operator returns it, otherwise it returns the expression to the right. Note that the right-hand side expression is evaluated only if the left-hand side is null.

For a block of code after the right-hand side expression:

   mutableProperty?.let {
            doSomething(it)
        } ?: run {
            doOtherThing()
            doOtherThing()
        }
Cumbayah
  • 4,415
  • 1
  • 25
  • 32
Bob
  • 13,447
  • 7
  • 35
  • 45
  • 2
    The disadvantage of this seems to be the limitation to a single method call on the RHS, as opposed to a block of code. Am I missing any trick on that count? – Rob Pridham Aug 25 '17 at 09:58
  • 1
    @RobPridham I guess you could technically use [`run`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/run.html) and use a lambda there instead, but that's a stretch – Salem Aug 25 '17 at 10:10
  • 1
    Yes. As @1blustone has mentioned, `run` or `let` can be used. Or you can just write a function block and invoke it by calling `.invoke()` or put `()` after the closing lambda, but this function won't be inlined. – Bob Aug 25 '17 at 10:15
  • 3
    I do not understand why this answer has so many upvotes. You are suggesting something and in the very next section you are proving your suggestion to be wrong. Read the doc one more time -> if the `mutableProperty` is not null it jumps into `doSomething(it)` and if this method returns `null`, then elvis operator executes `doOtherThing()`. – franta kocourek Jul 12 '18 at 19:09
  • 1
    You can use `also` in place of `let` to avoid the null problem. – tgeng Feb 01 '19 at 01:52
  • 4
    Blimey. What a convoluted mess. – RichieHH Jun 20 '19 at 04:03
38

I don't believe there is a really "short" way to achieve it, however you can simply use a conditional within with or let:

with(mutableVar) { if (this != null) doSomething(this) else doOtherThing() }
mutableVar.let { if (it != null) doSomething(it) else doOtherThing() }

In fact, "capturing" a mutable value is one of the main use cases of let.

This is equivalent to your when statement.

There is always the option you described, assigning it to a variable:

val immutable = mutableVar

if (immutable != null) {
    doSomething(immutable)
} else {
    doOtherThing()
}

which is always a nice fallback in case e.g. things get too verbose.

There probably isn't really a very nice way to achieve this because only the last lambda argument is allowed to be put outside the (), so specifying two wouldn't really fit the syntax of all of the other standard functions.

You could write one if you don't mind that (or if you'll be passing method references instead):

inline fun <T : Any, R> T?.ifNotNullOrElse(ifNotNullPath: (T) -> R, elsePath: () -> R)
        = let { if(it == null) elsePath() else ifNotNullPath(it) }

...

val a: Int? = null
a.ifNotNullOrElse({ println("not null") }, { println("null") })

Note that I would personally not do this, because none of these custom constructs are very pleasant to read. IMO: stick with let/run and fall back to if-else when necessary.

Salem
  • 13,516
  • 4
  • 51
  • 70
6

add custom inline function as below:

inline fun <T> T?.whenNull(block: T?.() -> Unit): T? {
    if (this == null) block()
    return this@whenNull
}

inline fun <T> T?.whenNonNull(block: T.() -> Unit): T? {
    this?.block()
    return this@whenNonNull
}

then you can write code like this:

var nullableVariable :Any? = null
nullableVariable.whenNonNull {
    doSomething(nullableVariable)
}.whenNull {
    doOtherThing()
}
zyc zyc
  • 3,805
  • 1
  • 14
  • 14
6

What about:

argument.mutableProperty
  ?.let { doSomething(it) } 
  ?: doOtherThing()
borjab
  • 11,149
  • 6
  • 71
  • 98
  • 5
    this might work in some circumstances, but be a problem, if e.g. doSomething() returns a value. in this case it would also call doOtherThing(), if doSomething() returns null – Mathias Henze Apr 22 '21 at 08:50
  • Are you trying to be cool, or trying to produce readable code? For the latter, you miss the mark. – Michael Piefel May 03 '23 at 12:19
6

I usually just do:

when(val it=argument.mutableProperty) {
    null -> doOtherThing()
    else -> doSomething(it)
}
Mathias Henze
  • 2,190
  • 1
  • 12
  • 8
5

Thanks to @zyc zyc , now I use this code

inline fun <T> T?.ifNull(block: () -> Unit): T? {
    if (this == null) block()
    return this@ifNull
}

inline fun <T> T?.ifNonNull(block: (T) -> Unit): T? {
    this?.let(block)
    return this@ifNonNull
}
// use
xxxx.ifNull {
    // todo
}.ifNonNull {
    // todo
}
aotian16
  • 767
  • 1
  • 10
  • 21
2

i usually write it like this:

  takeIf{somecondition}?.also{put somecondition is met code}?:run{put your else code here}

note the question mark after takeIf is a MUST. you can use also or apply keyword.

j2emanue
  • 60,549
  • 65
  • 286
  • 456
0

You could also do something like this:

class If<T>(val any: T?, private val i: (T) -> Unit) {
    infix fun Else(e: () -> Unit) {
        if (any == null) e()
        else i(any)
    }
}

You can then use it like this:

If(nullableString) {
   //Use string
} Else {

}
sunilson
  • 1,449
  • 15
  • 31