The Programmers Guide To Kotlin - If and When |
Written by Mike James | ||||
Monday, 05 July 2021 | ||||
Page 3 of 3
Nested If Versus WhenGiven 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") { 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 ExpressionsOne 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 { The Type Of A Conditional ExpressionThis 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
Summary
This article is an extract from: Programmer's Guide To Kotlin Third Edition
You can buy it from: Amazon Contents
<ASIN:B0D8H4N8SK> 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.
Comments
or email your comment to: comments@i-programmer.info |
||||
Last Updated ( Monday, 05 July 2021 ) |