Ofek Ron
Ofek Ron

Reputation: 8580

Kotlin ? vs ?.let {}

Consider this nice utility extension function i wanted to use :

inline infix fun <T> T?.otherwise(other: () -> Unit): T? {
    if (this != null) return this
    other()
    return null
}

It could be very useful for logging stuff when expressions evaluated to null for example:

val x: Any? = null
x?.let { doSomeStuff() } otherwise {Log.d(TAG,"Otherwise happened")}

but I see that it wont work for :

val x: Any? = null
x?.otherwise {Log.d(TAG,"Otherwise happened")}

see here for running example

Well when thinking about it i guess that makes sense that if x is null the ? makes the postfix not be executed, but i dont understand why the let in the first example is any different?

Is it possible to fix the utility to be more robust and work without having to have let in the chain?

Upvotes: 0

Views: 2368

Answers (3)

Adam Millerchip
Adam Millerchip

Reputation: 23091

First, you can simplify the implementation:

inline infix fun <T> T?.otherwise(other: () -> Unit): T? {
    if (this == null) { other() }
    return this
}

Or

inline infix fun <T> T?.otherwise(other: () -> Unit): T? =
    also { if (it == null) other() }

When you do this:

null?.otherwise { println("Otherwise happened") }

?. means "execute if not null", so otherwise is not executed.

What you need to write is:

null otherwise { println("Otherwise happened") }

Note this is very similar to the ?: operator (as Vadik pointed out in the comments):

null ?: println("Otherwise happened")

The difference is that otherwise always returns the value on the left (the same as also), but ?: returns the value on the right when the value on the left is null.

In my opinion, otherwise is confusing, especially as it always returns the left value despite the name. You would be better to use the ?: operator. Or perhaps rename it to something like alsoIfNull.

Upvotes: 3

HIGAN
HIGAN

Reputation: 76


x?.let { doSomeStuff() }.otherwise {Log.d(TAG,"Otherwise happened")}
// ⬇️
val value = if (x != null) {
    doSomeStuff()
} else {
    null
}
value.otherwise {Log.d(TAG,"Otherwise happened")}
x?.otherwise { Log.d(TAG,"Otherwise happened") }
// ⬇️
if (x != null) {
    otherwise { Log.d(TAG,"Otherwise happened") }
} else {
    null
}

?. means if the value is not null then execute the method and return the result otherwise return null

Upvotes: 0

Auroratide
Auroratide

Reputation: 2577

The let example executes because, when you don't utilize the infix feature, it looks like this:

x?.let {}.otherwise {println("1")}

Notice that it's not ?.otherwise; therefore, it always executes.

So to use otherwise without let, you can omit the ?.

x.otherwise { ... }

Upvotes: 2

Related Questions