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

All programming languages have to give you ways of modifying the flow of control -  making loops, conditional execution and so on. Kotlin uses a fairly traditional approach, but there are some exceptionally nice touches that if used correctly can make your programs much easier to understand.

Programmer's Guide To Kotlin Third Edition

kotlin3e360

You can buy it from: Amazon

Contents

  1. What makes Kotlin Special
  2. The Basics:Variables,Primitive Types and Functions 
  3. Control
         Extract: If and When 
  4. Strings and Arrays
  5. The Class & The Object
  6. Inheritance
  7. The Type Hierarchy
  8. Generics
  9. Collections, Iterators, Sequences & Ranges
        Extract: Iterators & Sequences 
  10. Advanced functions 
  11. Anonymous, Lamdas & Inline Functions
  12. Data classes, enums and destructuring
        Extract: Destructuring 
  13. Exceptions, Annotations & Reflection
  14. Coroutines
        Extract: Coroutines 
  15. Working with Java
        Extract: Using Swing
  16. Compose Multiplatform
        Extract: Compose Layout ***NEW!

<ASIN:B0D8H4N8SK>

The If With No Buts

The Kotlin if statement is very similar to the if statement in any modern language but with one small but beneficial twist. You can write an if statement in the usual way:

if ( condition ) statement

or you can use a compound statement to execute multiple statements

if (condition ) {
 statements
}

As always in Kotlin a compound statement is treated as a single statement and all Kotlin control statements work with single statements, and hence compound statements. 

For example you can write:

if(a>b) println(b)

or:

if(a>b) {
  println(a)
  println(b)
}

You can also use else to specify what happens when the condition is false:

if( condition ){
 statements
}else{
 statements
}

For example:

if(a>b){
 println(a)
}else{
 println(b)
}

 

You can, of course, use the if statement to build more complicated conditionals by nesting multiple if...else statements.

For example

if (a > b) {
        println(a)
    } else if (b > c) {
        println(b)
    } else {
        println(c)
    }

 

To be clear, there isn't a special elseif as there is in some other languages because you simply don't need it. Writing an if within an else clause gives you the same effect. The if will only be executed if the else clause is executed. 

This sort of nested if statement is fine, but if you nest if and else clauses too deeply it becomes confusing.

How deep is too deep?

When the structure becomes confusing to another programmer.

A nested if selects what is to be done on the basis of a tree of conditions. That is, a nested if is exactly equivalent to a decision tree. The first condition is the root of the tree and each condition divides the tree into a true and a false branch; the true corresponds to the if and the false to the else. The executable clauses are the terminal nodes of the tree and which one is executed depends on the path taken through the tree to a node.

A tree is a very compact representation of a decision process, but this compactness makes it very difficult to understand its total meaning.You can follow one branch easily enough but what about the others? 

You can always convert a tree into a set of sequential ifs by taking each branch and writing it as a condition with a single if. The result is a list of if statements with a clear condition that determines when it should be executed. This is a less compact way to write a complex decision but it is nearly always much clearer. Kotlin like many other languages has a construct that lets you write a sequential decision process very easily.

When?

One common way to simplify nested if statements is to use what in other language is called a select. In Kotlin a multiway conditional is called a when expression. A when matches its argument against each of the branches and executes the one that matches. If none match then an else clause can provide a default. 

For example

when(a){
        "s"->println("stop received")
        "r"->println("run received")
    }

 

In this case a is a String and its contents are matched against the examples. Note: a doesn't have to be declared as a string but it has to be initialized. You can use any type that equality makes sense for - String, Double, Ints and so on. Of course you aren't restricted to a single instruction following the -> as you can use a compound statement in curly brackets. 

Notice that there is no "fall through" typical of other languages. That is if a clause is executed then control passes to the instruction following the end of the when. There is no need for a break or any other exit instruction within each clause. The clauses are tested sequentially from the top and the first one to match is executed and this ends the when. 

If none of the clauses match then nothing happens and execution passes to the next instruction beyond the when. If you want a default clause then you can use an else. 

 

 when(a){
        1->println("stop received")
        2->println("run received")
    else -> println("unrecognized")
    }

 

This is the most basic when statement and if this is all it was capable of it would be useful but restricted to selecting actions based on a set of fixed discrete values. Fortunately the when statement has some more advanced aspects. 

You can for example combine selection values using a comma:

when(a){
        1,3->println("stop received")
        2,4->println("run received")
    else -> println("unrecognized")
    }

 

If any of the values matches the argument then the clause is executed and the when exited. Notice that these values are tested right to left and this provides a way of writing an or. 

You can also use an arbitrary expression to match against. So, for example:

when(a){
        2*2->println("stop received")
        c*b+5->println("run received")
    else -> println("unrecognized")
    }

 

You can use constants and variables and the expression is evaluated at the time that the when is executed.  As a slight extension, the expression can also be a range:

For example:

var a=8
when(a){
        in 1..10 ->println("1 to 10")
        !in 1..5 ->println("not 1 to 10")
       }

in 1..10 is true as the variable  a is in 1 to 10 and !in 1..5 is true if a is not in 1 to 5. You might, at first, think that the second message in the println is a typo and should be "not in 1 to 5" but it illustrates the scope for getting things wrong when writing even simple conditions. The first clause is executed if a is in the range 1 to 10 and then the when is exited. This means that the second clause is only tested if a isn't in the range 1 to 10. Thus the second clause only executes if a isn't in the range 1 to 10 as any value not in 1 to 10 is not in 1 to 5. Tricky isn't it! 

For more on ranges see the for loop later in this chapter:

The most general form of a when is using full conditional expressions on clauses. In this case each condition is evaluated from top to bottom and the first to evaluate to true causes its statements to be executed and the when to terminate. In this case the when doesn't need to have an argument and the selection is completely general and can be used to replace complex nested ifs. For example:

 when {
        a>=1 && a<=10 ->println("1 to 10")
        a<1 ||  a>5 ->println("not 1 to 10")
   }

This is the same as the previous range example and the first println is executed if a is in the range 1 to 10 and the second is printed if a is not in 1 to 10. Again the second condition is not a typo as described earlier.



Last Updated ( Tuesday, 27 June 2017 )