1. Overview
In this tutorial, we’ll discuss the usage of structural jump expressions in Kotlin.
Simply put, Kotlin has three structural jump expressions: return, break, continue. In the next sections, we’ll cover their functionalities with and without a label.
2. Labels in Kotlin
Any expressions in Kotlin can be marked with a label.
We create a label by using an identifier followed by the “@” sign. For example, abc@, loop@ are valid labels.
To label an expression, we simply add the label in front of it:
loop@ for (i in 1..10) { // some code }
3. The Break Statement
Without a label, break terminates the nearest enclosing loop.
Let’s have a look at an example:
@Test fun givenLoop_whenBreak_thenComplete() { var value = "" for (i in "hello_world") { if (i == '_') break value += i.toString() } assertEquals("hello", value) }
Alternatively, we can use break with a label, which terminates the loop marked with that label:
@Test fun givenLoop_whenBreakWithLabel_thenComplete() { var value = "" outer_loop@ for (i in 'a'..'d') { for (j in 1..3) { value += "" + i + j if (i == 'b' && j == 1) break@outer_loop } } assertEquals("a1a2a3b1", value) }
In this case, the outer loop is terminated when the i and j variables equal “b” and “1” respectively.
4. The Continue Statement
Next, let’s have a look at the continue keyword, which we can also use with or without a label.
Without a label, continue will proceed to the next iteration of the enclosing loop:
@Test fun givenLoop_whenContinue_thenComplete() { var result = "" for (i in "hello_world") { if (i == '_') continue result += i } assertEquals("helloworld", result) }
On the other hand, when we use continue with a label marking a loop, it will proceed to the next iteration of that loop:
@Test fun givenLoop_whenContinueWithLabel_thenComplete() { var result = "" outer_loop@ for (i in 'a'..'c') { for (j in 1..3) { if (i == 'b') continue@outer_loop result += "" + i + j } } assertEquals("a1a2a3c1c2c3", result) }
In this example, we’ve used continue to skip one iteration of the loop labeled outer_loop.
5. The Return Statement
Without a label, it returns to the nearest enclosing function or anonymous function:
@Test fun givenLambda_whenReturn_thenComplete() { var result = returnInLambda(); assertEquals("hello", result) } private fun returnInLambda(): String { var result = "" "hello_world".forEach { if (it == '_') return result result += it.toString() } //this line won't be reached return result; }
Return is also useful when we want to apply continue logic on anonymous functions:
@Test fun givenAnonymousFunction_return_thenComplete() { var result = "" "hello_world".forEach(fun(element) { if (element == '_') return result += element.toString() }) assertEquals("helloworld", result) }
In this example, the return statement will return to the caller of the anonymous fun, i.e. the forEach loop.
In the case of a lambda expression, we can also use return with a label to achieve a similar result:
@Test fun givenLambda_whenReturnWithExplicitLabel_thenComplete() { var result = "" "hello_world".forEach lit@{ if (it == '_') { return@lit } result += it.toString() } assertEquals("helloworld", result) }
Alternatively, we can also return using an implicit label:
@Test fun givenLambda_whenReturnWithImplicitLabel_thenComplete() { var result = "" "hello_world".forEach { if (it == '_') { // local return to the caller of the lambda, i.e. the forEach loop return@forEach } result += it.toString() } assertEquals("helloworld", result) }
In the example above, the return statement will also return to the caller of the lambda – the forEach loop.
Finally, return can be used with a label to apply break logic to lambda expressions by returning to a label outside:
@Test fun givenAnonymousFunction_returnToLabel_thenComplete() { var result = "" run loop@{ "hello_world".forEach { if (it == '_') return@loop result += it.toString() } } assertEquals("hello", result) }
6. Conclusion
In this article, we have gone through the use cases of return, break, continue in Kotlin.
The sample code could be found over on Github.