What parameter type should I use instead of Nothing
in such construction to don’t add anything in a collectible?
fun ret(foo:Int) = listOf(when (foo) {
1 -> "ok"
2 -> "not ok"
else -> Nothing //??? return listOf()
})
What parameter type should I use instead of Nothing
in such construction to don’t add anything in a collectible?
fun ret(foo:Int) = listOf(when (foo) {
1 -> "ok"
2 -> "not ok"
else -> Nothing //??? return listOf()
})
That’s not possible.
listOf
with a single argument will - understandably - create a list with a single argument, no matter what that argument is.
You could use null
and then call .filterNulls
but that’s a meh solution, I’d rather just put the listOf
inside the when
rather than outside.
It looks like a possibility for compiler improvement to shorten the code.
I don’t think this is a good idea. This code explicitly asks for a single-item list, it would be pretty confusing if in some cases it could actually return an empty list.
As @al3c said, you can put listOf()
inside when
(definitely preferred by me - both the most readable and the best for performance), you can use listOfNotNull()
or use some kind of a list builder, e.g. buildList {}
.
That’s the right thing here - I didn’t think of that.
listOfNotNull()
adds N complexity - it’s a simple checking loop, not good. And I didn’t find such method for a map
, is it available?
What about the case:
fun all() = children.plus(parents).plusElement(spouse ?: **Nothing**)
As above, it is not possible. By writing this code you first say you want to invoke plusElement()
/ pass an expression to listOf()
and then you would like to somehow undo what you just said.
I’m not sure what do you mean here. What is N
?
N is a list size.
And what is the size of a list of a single item?
It’s not only about an empty list but also adding to current (case 2 e.g.).
Yes, it just need to make return
inside the method.
Remember, we don’t write code for computers, we write it for humans.
This single use case is not broad or impactful enough to warrant a compiler change. Multiple good alternatives exist.
One alternative is this:
val myList: List<String> = when(number) {
1 -> listOf("ok")
2 -> listOf("not ok")
else -> listOf() // Or better yet, use emptyList()
}
This first example may read more clearly to some (it does to me).
However, I’d bring up the real value is in making it easier to change the logic of the list formation. Changing the behavior on bad values, changing the list formation strategy for one option, adding some validation to an option.
For example, maybe I decide I want to call a named list function to fill the values for option “2”, 2 -> fetchInvalidCode()
.
For another example, you might use emptyList()
for the else
case since you want to express your intention as the author that you did in fact mean to leave that list as empty and it isn’t a typo.
This might not apply to your case now but what we’ve done is reduced the code’s “resistance to change”, which is the definition of technical debt.
Another thing to consider is “true duplication” vs “coincidental duplication”. True duplication is when the duplicates shall always change at the same time and for the same reason. Coincidental duplication is where the duplicates could potentially change at different times or for different reasons (even if they’ve been synced up so far).
In your example, you using a single listOf
call which removed the duplicated listOf
. I would suspect that the method by which you form these lists are not true duplication and you should feel comfortable with multiple listOf
calls.
Basically, the temptation to remove duplication is often misleading.
Of course, there are more alternatives such as collection builders that @broot mentioned. Collection builders are a good fit for simple use-cases and more complex ones.
And lastly you could use .filterNotNull()
as others mention. This would be an O(N) operation technically. However, in reality, on average it would not be worth worrying about performance. There’s a danger in “pre-optimizing” where you spend time making a 1-2% gain when elsewhere in your code you could be solving an easier problem that gains 10-20%.
Using filterNotNull()
is big-O of N, but in this case N=1
getListOfOneElement().filterNotNull() // O(1), since N=1
It’s good to understand the performance and the big-O impact of your code, however, it’s just as important to write expressive and easy-to-change code for humans.
In this case O(N), where N is a literal string list or even an on-average small dynamic list, you’ve already lost more performance typing it up in this forum than you will ever see in removing that O(1) operation.
Kotlin has a clear and expressive set of functional transformations. I’d recommend not shying away from them in order to avoid microscopic performance costs when you can gain word-like clarity of what is happening.
There could be other use cases that support when
to be modified but this example isn’t strong enough on its own.
Another argument in favour of listOfNotNull()
is that it works equally well with more than one optional item. For example:
val list = listOfNotNull(
fixedValue,
someFunctionThatMayReturnNull(),
value.takeIf{ someCondition },
value.takeIf{ someOtherCondition },
// ...
)
Doing that with listOf()
or other means would almost certainly involve more processing and more temporary lists, as wel as being more long-winded and harder to read and maintain.
Erm… how about just simply null-checking before adding the element, like for example so:
fun all() = spouse?.let { children.plus(parents).plusElement(it) } ?: children.plus(parents)
But honestly, I think you should just use listNotNull.
Looking at the first piece of code you posted, the added complexity doesn’t matter because you have a size of one. Looking at the second example, the added complexity does not matter because you’re copying a list already anyways, so you can’t be that pressed for performance.
Overall, I can’t see the complexity matter unless your list has a size in the millions of elements and you have very strict performance restrictions. listNotNull is the function intended to be used for the purpose you’re asking about.
For the sake of answering the original question, you can technically do this:
fun ret(foo:Int) = listOf(when (foo) {
1 -> "ok"
2 -> "not ok"
else -> return listOf() // Literally the answer was in your comment all along :)
})
But obviously, as everyone above said, please don’t do thid because it ruins clarity
Actually, tried it with
Returns are not allowed for functions with expression body. Use block body in '{...}'
But this works:
fun ret(foo:Int): List<String> { return listOf( when (foo) { 1 -> "ok" 2 -> "not ok" else -> return listOf() } ) }
Looks ok, just the second case is unresolved:
fun all() = children.plus(parents).plusElement(spouse ?: **Nothing**)
Ok, I just found, it’s possible to use this simple form of listOfNotNull
with small structures :).
public fun <T : Any> listOfNotNull(element: T?): List<T> = if (element != null) listOf(element) else emptyList()