This code is from an Udemy-course I'm currently doing:
if (noteTitle.isNullOrEmpty()) {
title_et.error = "Title required"
return#setOnClickListener
}
What it is basically doing is clear to me. But what's that 'return#setOnClickListener' statement?
What's the meaning of these #method-name syntax? What means the #-character here?
Its qualified returns.
Consider the following code:
fun foo(strings: List<String?>) {
strings.forEach {
if (it.isEmptyOrNull()) return
println(it)
}
}
Since forEach is an inline function, return statement will return from the parent function foo instead.
To simply return from one iteration of forEach you qualify to return only on a particular scope:
strings.forEach {
if (it.isEmptyOrNull()) return#forEach
// ...
}
You can customize it with other name if they are ambiguous:
strings.forEach outer# { a ->
list2.forEach inner# { b ->
if (a.isEmptyOrNull()) return#outer
// ...
}
}
This is useful mostly in inline and cross-inline situations, but sometimes you also want to explicitly qualify in non-inline lambdas (just to be more precise) as in the example you mentioned.
Related
The Code A use for (aMovice in listVoiceBySort.value!!) to access each element of LiveData<List<MVoice>>.
But I don't think it's a good way because it uses listVoiceBySort.value!!, it's ugly.
Is there a good way to access each element of LiveData<List<MVoice>>?
Code A
val listVoiceBySort: LiveData<List<MVoice>> =_listVoiceBySort
fun selectAllIDs(){
for (aMovice in listVoiceBySort.value!!){
selectedIDs.add(aMovice.id)
}
}
It would be better to use forEach i.e. an inline function with the same functionality.
It lets you use safe calls (?.) instead of not null assertion (!!):
fun selectAllIDs() {
listVoiceBySort.value?.forEach {
selectedIDs.add(it.id)
}
}
Another way is to map MVoice elements to their ids and then add them all to the selectedIDs:
fun selectAllIDs() {
listVoiceBySort.value?.map { it.id }?.let {
selectedIDs.addAll(it)
}
}
Generally data inside a livedata is accessed via an observer where you get a non-null value
listVoiceBySort.observe(lifecycleOwner, Observer {
for(mVoice in it){
...
}
}
I wonder how to skip one entry while mapping or filtering?
fun getFilteredList(list: List<String>, match: String): List<String>? {
val flist = list.filter {
try {
// Some parsing logic which can throw exceptions
val res = SomeParser.parse(match)
it == res
} catch (e: Exception){
// I want to skip this entry to include in flist but still continue
// for left of the list entries without return. How to do that?
return null
}
}
return flist
}
The problem is your use of the return keyword. Your code should look like:
// return type no longer needs to be nullable
fun getFilteredList(list: List<String>, match: String): List<String> {
val flist = list.filter {
try {
val res = SomeParser.parse(match)
it == res
} catch (e: Exception){
true
}
}
return flist
}
Or, based on your example, just (using "single-expression function" syntax):
fun getFilteredList(list: List<String>, match: String) = list.filter {
try {
it == SomeParser.parse(match)
} catch (ex: Exception) {
true
}
}
Note: Both of these will call SomeParser.parse for each element in the list. However, you mention this is just an example and the real code works differently (i.e. can't be pulled out of the filter operation).
The reason return was giving you an error has to do with lambda expressions. From the Returning a value from a lambda expression section of the Kotlin reference:
We can explicitly return a value from the lambda using the qualified return syntax. Otherwise, the value of the last expression is implicitly returned.
Therefore, the two following snippets are equivalent:
ints.filter {
val shouldFilter = it > 0
shouldFilter
}
ints.filter {
val shouldFilter = it > 0
return#filter shouldFilter
}
[...]
And this is in combination with the fact filter is an inline function. From the Non-local retuns section of the Kotlin reference:
In Kotlin, we can only use a normal, unqualified return to exit a named function or an anonymous function. This means that to exit a lambda, we have to use a label, and a bare return is forbidden inside a lambda, because a lambda cannot make the enclosing function return:
fun foo() {
ordinaryFunction {
return // ERROR: cannot make `foo` return here
}
}
But if the function the lambda is passed to is inlined, the return can be inlined as well, so it is allowed:
fun foo() {
inlined {
return // OK: the lambda is inlined
}
}
Such returns (located in a lambda, but exiting the enclosing function) are called non-local returns. [...]
This means when you had return null it was actually trying to exit the entire getFilteredList function. I'm assuming this is why you have your return type as List<String>? instead of List<String>. And when you tried return false you were trying to return a Boolean from a function whose return type was List<String>?.
Back in java I used to write only return for a void method... But kotlin doesn't seem to allow just return, instead it uses return#methodname?
Can someone explain what this is and how does it add value?
bAddLine.setOnClickListener {
val selectedSeries = getSelectedSeries()
if (selectedSeries.isEmpty()) {
Toast.makeText(this, getString(R.string.toast_channel_mandatory), Toast.LENGTH_LONG).show()
return#setOnClickListener
}
}
From kotlinlang website:
Return at Labels
With function literals, local functions and object expression, functions can be nested in Kotlin. Qualified returns allow us to return from an outer function. The most important use case is returning from a lambda expression. Recall that when we write this:
fun foo() {
ints.forEach {
if (it == 0) return // nonlocal return from inside lambda directly to the caller of foo()
print(it)
}
}
The return-expression returns from the nearest enclosing function, i.e. foo. (Note that such non-local returns are supported only for lambda expressions passed to inline functions.) If we need to return from a lambda expression, we have to label it and qualify the return:
fun foo() {
ints.forEach lit# {
if (it == 0) return#lit
print(it)
}
}
Now, it returns only from the lambda expression. Oftentimes it is more convenient to use implicits labels: such a label has the same name as the function to which the lambda is passed.
fun foo() {
ints.forEach {
if (it == 0) return#forEach
print(it)
}
}
When inside a lambda, you have to specify which scope you wish to return from, because it might be ambiguous. See the official docs about returning at labels.
In this specific case, if you'd be returning at the end of a function that returns nothing, you can omit the return statement altogether.
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?
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()
}
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.
What about:
argument.mutableProperty
?.let { doSomething(it) }
?: doOtherThing()
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()
}
I usually just do:
when(val it=argument.mutableProperty) {
null -> doOtherThing()
else -> doSomething(it)
}
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.
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
}
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 {
}
I'm a newbie to Kotlin, but I freaking love it. There's a wonderful feeling when you can turn three lines of code into one. Now I often find myself looking at my code and thinking "there's some redundancy here. There should be a more concise way to do it." And when I do some searching, often Kotlin does provide a more succinct way.
Here's a problem where I FEEL there should be a simple, concise Kotlin solution, but am not aware of it. Hopefully you can enlighten me!
Take code like this contrived example:
fun doSomething(): Boolean {
if (randomInt % 2 == 0) {
foo = Foo()
true
} else {
bar = null
false
}
}
Based on an if condition, I want to run some code and then return the value of the condition. It just bothers me that I have to explicitly say "if the condition is true, return true. If it is false, return false." It seems redundant. Of course, I could say return randomInt % 2, but I want to run code based on if it is true.
Is there a magic operator Kotlin has that I don't know about? Should I make a function to handle this situation for me, although the syntax of calling that would be different than an if statement? Is there some kind of mapping function, or some way to overload if? It seems like there should be a concise, clever answer, but it's not coming to me.
You can refactor your code a bit so that the return and code happen in different places:
fun doSomething(): Boolean {
val isEven = randomInt % 2 == 0
if (isEven) foo = Foo() else bar = null
return isEven
}
I upvoted some of the answers. I should have considered splitting out the condition to a variable or having the true/false test after the condition.
But after sleeping on it, I have another solution.
New utility functions:
fun Boolean.onTrue(block: () -> Unit): Boolean {
if (this) block()
return this
}
fun Boolean.onFalse(block: () -> Unit): Boolean {
if (!this) block()
return this
}
And then my original code sample can be condensed to:
fun doSomething() = (randomInt % 2).onTrue { foo() }.onFalse { bar = null }
This is the most concise option, although it has its own disadvantages. Nothing prevents the user from, say, calling onTrue() twice or calling onFalse() before onTrue(). It looks quite different from a standard if/else, and if you use both paths, both onTrue() and onFalse() have to check the condition. And then, of course, there's remembering to use the new utility function instead of standard Kotlin operators. Still, it has an appealing brevity. I'll be interested to see what other people think.
You may explore some useful extension functions from Kotlin's standard library. For example, you may use apply:
/**
* Calls the specified function [block] with `this` value as its receiver and returns `this` value.
*/
public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
Here, randomInt % 2 == 0 will be the return value of apply.
fun doSomething(): Boolean = (randomInt % 2 == 0).apply { if (this) foo = Foo() else bar = null }
Updates: If you prefer a more readable code, it is not a good solution. Have fun :)
It's actually quite simple, you just have to get used to Kotlin's fantastic stdlib functions like apply, with and let. In this case, apply is what you need:
fun idiomatic(myInt: Int) = (myInt % 2 == 0).apply {
if (this) println("is equal") else println("in not equal")
}
What happens: apply is invoked on Any object, a Boolean (the condition) in this case, which directly becomes the functions receiver, referred to as this. The important thing is, that apply will, after the supplied code block has been executed, return its receiver, which is your Boolean here.
I hope this is what you need!
#pixel-elephant's solution looks concise and good.
However, from the clean code perspective, your doSomething() function is doing two things.
If possible you can separate it out to two functions and move the if check to the topmost function:
if ( randomInt.isEven() ) {
doSomethingEven()
// ...
} else {
doSomethingOdd()
// ...
}