The Programmers Guide To Kotlin - If and When
Written by Mike James   
Monday, 05 July 2021
Article Index
The Programmers Guide To Kotlin - If and When
The When
IF V When

 

Nested If Versus 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

This makes the condition that results in a discount of 0.08 completely clear and removes the dependence of the when on the order of the clauses.

It is a general principle that you should try to write conditions so that the order of the tests doesn’t matter. This gives you protection from an innocent programmer accidentally changing the order of the conditions while, say, adding to them, without realizing that it matters.

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

If & 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:

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

You can use when in the same way:

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

The Type Of A Conditional Expression

This is slightly advanced, you can return to it after reading Chapter 7 on Type.

Notice that if you use if or when as an expression then the type of the result is the type that just includes the possible results – in the documentation this is referred to as the least upper bound. The idea is that it uses the type that is able to represent the possible results, but no additional ones, for example:

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

In this case x inferred to be an Int – which is reasonable and obvious, but what about:

var x= if(a>1) 1 else “Less Than”

In this case x is of type Any as this is the only type that can reference both Int and String. However, you can’t treat x as an Int or a String unless you cast to the appropriate type. For example:

var a = 10
var x = if (a > 1) 1 else "less than"
x = x + 1
println(x.length)

will produce a compile-time error because you cannot add one to or find the length of an object of type Any. Notice that this is true even though the compiler could work out at compile time that x is a String.

If you need to use mixed types in a conditional expression you probably need to use something like:

var x = if (a > 1) 1 else "less than"
if (x is Int) x = x + 1
if (x is String) println(x.length)

where each use of x is protected by specifying the type that it is referencing and automatically casting to that type.

The same ideas apply in a more general context. If conditional expressions can return an object of different type then the variable is inferred to be the type that is just general enough to reference all of the possible objects.

There is a final extreme case we need to consider. If a conditional expression doesn’t return a result then the expression itself is of type Unit and cannot be used in an assignment. For example:

var x = if(a>1) 1

generates a compile time error to the effect that the conditional expression of type Unit cannot be used in an assignment.

The same rule applies to when, but in this case it can be more difficult to work out if it is complete or exhaustive in the sense that it always returns a value. If in some cases it doesn’t return a value then, like the if expression, it cannot be used in an assignment. The simplest rule is that a when expression is exhaustive if it has an else entry. If this isn’t the case then things get more complicated. If the bound expression is an enum or a sealed class and all of its possible values are tested and return a value then it is exhaustive. There are also some complicated edge cases that you can mostly ignore.

In chapter but not in this extract

  • The While Loop
  • Nested Control With Break & Continue
  • Labels
  • The For Loop
  • Kotlin Control

Summary

  • The Kotlin if is much like what you would find in other languages. You can have if, else and else if clauses.

  • If nesting of if clauses becomes deep, try using the when statement. This also allows you to convert a nested structure into a “flat” set of mutually exclusive tests.

  • A when can test for equality, general expressions, ranges, logical conditions, object equality and type.

  • Both when and if can be used as expressions returning the last value computed.

  • As an expression, if does away with the need for a ternary operator.

  • The while loop executes zero or more time and the do while loop executes one or more times.

  • The control break can be used to abort a loop – execution continues as if the loop had finished.

  • Use continue to abort one iteration of a loop.

  • Statements can be labeled and break and continue can use labels to abort outer loops and loop iterations.

  • The for loop always takes the form of an iterator through a collection object.

  • To construct the more familiar C/C++/Java for loop with an index, you can make use of range expressions.

This article is an extract from: 

Programmer's Guide To Kotlin Second Edition

kotlin2e360

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 ***NEW!
  4. Strings and Arrays
  5. The Class & The Object
  6. Inheritance
  7. The Type Hierarchy
  8. Generics
  9. Collections, Iterators, Sequences & Ranges
  10. Advanced functions 
  11. Anonymous, Lamdas & Inline Functions
  12. Data classes, enums and destructuring
  13. Exceptions, Annotations & Reflection
  14. Coroutines
        Extract: Coroutines 
  15. Working with Java

<ASIN:1871962706>

<ASIN:B096MZY7JM>

To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.

Banner


A Robot That Sticks To the Ceiling By Vibrating!
11/07/2021

This is a nice example of how unexpected effects can be put to good use. Well I think it's unexpected, and I also think that the mechanism behind how this works isn't very clear.



ES2021 Improves Promises Support
01/07/2021

The twelfth edition of ECMAScript, ES2021, has been approved with improved support for promises and new logical assignment operators.


More News

square

 



 

Comments




or email your comment to: comments@i-programmer.info



Last Updated ( Monday, 05 July 2021 )