Elvis operator with Run in kotlin - kotlin

Im having a bit of trouble with this situation
a?.let {
b?.let { }
}?: run { }
The thing is, if "b" is null, the run block is executed, even though the elvis operator is referencing the "a" let.
I already tried to use "apply" instead of "run", same thing happens

The reason is that the let function returns whatever its last expression is. If the last expression evaluates to null (as b?.let would if b is null or the last line of that inner let evaluates to null), then the second part of the Elvis operator will be evaluated.
The solution is to never follow up a scope function call with an Elvis operator. It would work OK with also instead of let since it doesn't return the lambda result, but it's still obtuse-looking code that's hard to read. It's such an ugly pattern to use that people make memes about how ridiculous it is.
For this particular case, I would refactor your code as follows.
val a = a
if (a != null) {
b?.let {
//...
}
} else {
//...
}

Related

Pass no value to function with default parameters

I have this Kotlin function:
fun doSomething(user: User = defaultUser) {
//do something
}
and I call it from another place:
val user: User? = getUser()
if (user == null) {
doSomething()
} else {
doSomething(user)
}
Is it possible to improve this code? I think this "if/else" is a little bit messy. Is possible to do something like this?
doSomething(user ?: NoValue)
You can cut it down to user?.run(::doSomething) ?: doSomething() (if doSomething doesn't return null) but I don't know why you'd want to!
Honestly the if/else reads nice to me, stick it on one line without the braces and it's nice and compact. Unfortunately I don't think you can conditionally add parameters into a function call (and handling default parameters can get unnwieldy when you have a few).
I agree with #benjiii, it might be better to have a nullable parameter and handle the default internally, if you don't need to use null as a legit value
You could do something like this:
getUser()?.let { // user is not null
doSomething(it)
} ?: run { // user is null here
doSomething()
}
(cf: Swift 'if let' statement equivalent in Kotlin)
I don't think you could do something shorter without making the code hard to understand Edit 2: Actually you can, see the comment
Edit: I would personally handle the nullable variable inside the function like this:
fun doSomething(user: User?) {
val correctUser = user ?: defaultUser
//do something
}
so you can use the function like this:
doSomething(getUser())
I agree with cactustictacs, just putting it on one line is clear and simple. However, if you use it often and it's bothering you, it's easy enough to wrap it in a function without the default parameter:
fun doSomethingSensibly(user: User?) =
if (user == null)
doSomething()
else
doSomething(user)
Which can be used as:
doSomethingSensibly(getUser())

Simplify testing of a null variable in an IF statement

In Kotlin I have this (which will not compile):
var list: MutableList<String>? = null
if (list.isNotEmpty()) {
}
This will compile:
var list: MutableList<String>? = null
if (list!!.isNotEmpty()) {
}
However, if list is null, a runtime exception will occur. I could do this:
var list: MutableList<String>? = null
if ((list != null) && list.isNotEmpty()) {
}
But this seems to be repetitive everywhere you need to test if something is null. Is there a more eloquent way of doing this in Kotlin?
In the specific case of checking if the list is not null or empty you can use:
if (!list.isNullOrEmpty())
For a list, it's better to avoid handling null state instead handle only empty and non-empty state. refer http://thefinestartist.com/effective-java/43.
Saying that, we don't need to explicitly check for null check and only empty check alone should do the trick.
var list : MutableList<String> = mutableListOf()
list.add("Test1")
list.takeIf { it.isNotEmpty() }?.forEach { println(it) }
We can use
takeIf
to check whether the list is empty or not.
The first way add this line
list = list?:MutableList<String>()
Second way
val isEmpty = list.isEmpty()?:false
if(isEmpty){}else{}
Third way
if (!list.isNullOrEmpty())
as #AndroidDev suggested
Why are getting an error? Since !! require non-null. if the object is null then it will throw NPE
I think most can be done with the safe operator ?. itself. So if you just want to iterate over the list (or reduce, map, or whatever), you can just simply do so directly:
val sumExpression = list?.joinToString("+") { it.someValue } ?: throw exception? use default value?
list?.forEach { println("an entry in the list: $it") } // if there are entries, just iterate over them... if not, there is nothing to do
list?.also {
consume(it) // consume now is only called if the list is not null
}
Enhancing it then with a condition is also rather easy with takeIf or takeUnless (depending on what you require):
list?.takeIf { it.isNotEmpty() }
Now you still have a nullable list and can again call any of the desired functions as shown before:
list?.takeIf( it.isNotEmpty() }?.also {
consume(it)
}
Also a variant instead of list.isNullOrEmpty() (already shown by gpuntos answer) is to compare the possible null value to the expected outcome, e.g.:
if(list?.isNotEmpty() == true) {
// do something with the non-empty list
}
In case it is null the condition simplifies to basically null == true which is false. However I wouldn't overuse that too much as then you don't see the actual nullable types so easily anymore. Such a usage may make sense, if what you are trying to calculate isn't already supported in its own extension function (as is with isNullOrEmpty()), e.g. for entering the if only if the count is 4 it makes sense to use something like: if (list?.count() == 4)...

Kotlin let versus != null

What is the difference between using kotlin's object?.let {...} versus if(object != null) {...}? I've run into the situation where using let on a non-null equates to false and the block is not executed but using the if statement with the same object results in properly identifying that the object non-null and the block is executed.
Do they differ on the low level somehow?
Under the hood, object?.let { } will be compiled to if (object != null) { }. You can try to check the kotlin bytecode.
IMHO, using let also may benefit you to chaining function which makes your code declarative, and I think it is more readable.
One of the scenario where let keyword is preferable when you are reading the list of items that also contains null values and you wants to print only not-null values.
For Example:-
var array = arrayOf("StackOverflow", "Kotlin", "Android", null, "Jetpack", null)
for (item in array) {
item?.let {
print("$it ")
}
}
Output:
StackOverflow Kotlin Android Jetpack
Code inside for loop is like saying "if item is not null, let's print its value". Therefore ?.let allows you to run code for a value that's not null.

kotlin - run vs elvis operator - what is the difference?

im trying to understand the difference between the following two pieces of code in kotlin:
myVar?.let { print(it) } ?: run { print("its null folks") }
vs
myVar?.let { print(it) } ?: print("its null folks")
are they equivalent ? is run just so we can use a block of code and the the other is for just a single statement ?
Yes, they are equivalent. run allows you to use multiple statements on the right side of an elvis operator; in this case there's only one, so run is not needed.

Option Chaining instead of if/else

Is there a more succint way to write the following code using option chaining and/or the elvis operator?
email.addSubject(if (creator != null) String.format( inviteDescription, creator) else String.format(inviteDescriptionNoCreator, group))
It feels like there should be.
Using the normal IF expression
val subject = if (creator != null) {
inviteDescription.format(creator)
} else {
inviteDescriptionNoCreator.format(group)
}
email.addSubject(subject)
The Elvis Operator
val subject = creator?.let {
inviteDescription.format(it)
} ?: inviteDescriptionNoCreator.format(group)
email.addSubject(subject)
If the goal is to write the shortest code possible then you could go with a single line Elvis operator. But if the goal is a readable code, I would choose the simple if expression or a multi line Elvis operator. I would even go one step ahead and move it to a separate method. But whatever you choose, please don't write everything in a single long line, rather put it in separate lines.
Just taking advantage of ?. and ?: gives us the following:
email.addSubject(creator?.let { String.format(inviteDescription, it) } ?: String.format(inviteDescriptionNoCreator, group))
Unfortunately, that's still quite long and is arguably not much easier to read. You could shave off a bit more by using the String.format extension function:
email.addSubject(creator?.let { inviteDescription.format(it) } ?: inviteDescriptionNoCreator.format(group))