The Programmers Guide To Kotlin - Control
Written by Mike James   
Tuesday, 27 June 2017
Article Index
The Programmers Guide To Kotlin - Control
If and When as Expressions
Kotlin's For Loop

Testing For Type 

You can even use objects, but in this case the test is for equality of reference, not type. That is, the clause that is executed is the one that has a reference to the same object. For example:

when(obj1){
     obj2->println("same object")
    else->println("different object")
}

prints same object if obj1 and obj2 reference the same object, i.e. if obj1==obj2.

If you want to test for equality of type then you have to use the fact that you can use a general expression. For example:

when(obj){
 is MyClass1 ->obj.doMethod()
 }

Nested If v when

Given the flexibility in specifying the condition within a when statement, you can often write a deeply nested if statement much more clearly using when. Take a simple situation with three products A, B and C. For A the discount is 0.1; for B costing less than $100 the discount is 0.05 and 0.08 otherwise; and for C the discount is 0.2. All other products have zero discount. 

First let's implement a nested if:

if (product == "A") {
        discount = 0.1
    } else if (product == "B") {
        if (cost < 100) {
            discount = 0.05
        } else {
            discount = 0.08
        }
    } else if (product == "C") {
        discount = 0.2
    } else {
        discount = 0.0
    }

Good luck with pairing up the brackets. I'm not claiming that this is the best way to nest conditions, just that it is typical.

You can see that this nested if is a decision tree and this can be "flattened" into a sequential decision process and implemented using a when. 

Compare this nested if to the equivalent when:

when {
    product == "A" -> discount = 0.1
    product == "B" && cost < 100 -> discount = 0.05
    product == "B" -> discount = 0.08
    product=="C"->discount=0.2
    else -> discount=0.0
}

It is a simple list of the conditions needed for each discount. Although it isn't needed it is clearer to write the second condition on product B as

product==B && cost>=100 -> discount=0.08

to make the condition that results in the the 0.08 discount completely clear and to remove the dependence of the when on the order of the clauses. 

In most cases a when is preferable to a deeply nested if structure.

If and When as Expressions 

One very important point about the if and the when statements is that they can both be used as expressions and they return the last value calculated. The only restriction is that an if or when statement used as an expression has to have an else part. For example:

var x=if (a>1) 1 else 0

or

var x=when{
          a>0-> 1
 
        else-> 0
}

 

If you call a function then the result will be the value of the function or unit if the function doesn't return a value. 

The fact that you can use the if statement as an expression means that there is no need for a ternary operator as the if expression does exactly the same job. In Java you might write:

x=a>1 ? 1,0

which is exactly the same as the more readable Kotlin:

x= if(a>1) 1 else 0 

The While Loop

Kotlin has only three loops - the while, do while and the for, but this is more than enough. They each work much as in other languages, but of the three the for is the most interesting. 

The while and do while loops are conditional loops. The while has the condition start of the loop and the do while has it at the end.  For example:

while(x>0){
 do something
}

is a while loop that will repeat while x is greater than zero, and:

do{
 do something
} while(x>0)

is a do while loop that will also repeat while x is greater than zero.

What is the difference?

The answer is that if x is already zero or less when the loop starts the while loop will not be executed at all, but the do while loop will be executed once. This corresponds to the test for the while being at the start of the loop and for the do while at the end of the loop.

Put another way, a while loop can repeat zero or more times and a do while loop can repeat one or more times. 

Break & Continue

While and do while are simple and easy to understand and if possible you should try to use them as they are. However, there are times when a problem is easier to solve by jumping out of a loop in the middle of the block of code rather than just at the start of the end. You can do this using the break statement. The Kotlin break statement goes beyond what you might have encountered in other languages. At its simplest, break brings a loop to an end. For example:

while(x>0){
  do something 1
 if(condition) break 
  do something 2
}

 

In this case the loop will exit when the if statement's condition is true. When the loop exits via the break the instructions before the break will have been executed once more than the instructions after the break, i.e do something 1 will execute one more time than do something 2.

Continue works like break, but it causes the loop to abandon the current iteration and start over. If the break statement is like a jump out of the loop to the instruction following, the continue is like a jump back to the start of the loop.

For example:

while(x>0){
  do something 1
 if(condition) continue
  do something 2
}

In this case the loop moves on to the next iteration if the condition is true. This means that do something 1 might be executed any number of times more often than do something 2. In fact if the condition is always true, do something 2 never gets executed. 

Labels

Break and continue are two of the three nested control statements that are present in Kotlin, the other being return. These are nested control statements because they can appear in a situation where they are nested within other control structures. You can, for example, place a break in the inner loop of a set of nested loops.

Break and other nested flow of control statements are more sophisticated than you might think.

First the shock news - Kotlin has labels.

In Kotlin you can assign a label to any expression. A label takes the form of an identifier ending in @ and all you have to do is place the label in front of the expression.

The good news is that statement labels aren't used for anything as dangerous as an unconstrained goto or other jump instruction. Instead they serve the purpose of identifying which of a set of nested constructs is being referred to. This works with the break and the continue simply enough, but its use with return is more complicated and depends on understand inline functions, more of which in a later chapter. 

For example, if you have a set of nested loops, the break with a trailing label will break out of all of the loops to the level of the loop with that label. That is, execution continues as if the labeled loop had terminated. 

For example consider: 

   while (i < 10) {
        i++
        j = 0
        while (j < 10) {
            j = j + 1
            print(i)
            print(",")
            println(j)
            if (j > 4) break
        }
    }

In this case the inner loop breaks when j equals 5 and so you see i from 1 to 10 and j from 1 to 5. If we label the outer loop and the break the behavior is different:

    var i = 0
    var j = 0
    loop@ while (i < 10) {
        i++
        j = 0
        while (j < 10) {
            j = j + 1
            print(i)
            print(",")
            println(j)
            if (j > 4) break@loop
     
  }
    }

Before the break is executed when j equals 5 as before, but now the break ends both the inner and outer loop and execution continues after the outer loop, i.e. the one labeled loop@.

The same idea applies to continue and return.  For continue the situation is exactly the same as for break in that the loop that is restarted is the one labeled. Notice that this means that the inner loops terminate. For example:

    var i = 0
    var j = 0
    loop@ while (i < 10) {
        i++
        j = 0
        while (j < 10) {
            j = j + 1
            print("j=")
            println(j)
            if (j > 4) continue@loop

        }
        print("i=")
        println(i)
    }

In this case the outer loop is intended to print a value for i and the inner loop prints a value for j, but because of the continue@loop the outer loop never prints the value of i. The reason is that the inner loop's continue@loop restarts the outer loop and hence aborts the inner loop. Restarting the outer loop means that its print statements are never executed.

Using break and continue with loops is sometimes convenient, but you should always consider what makes for easy to read code. There is a strong argument that break and continue only make sense in a very limited range of situations which apply more to for loops than to conditional loops. 



Last Updated ( Tuesday, 27 June 2017 )