If/When As Expressions

In Kotlin, if and while seem like they are fairly similar to their equivalents in other languages. when is a bit different but it still functions a lot like switch in Java, etc.

Where things start to get interesting is when you realize that if and when not only offer branching, but that they are expressions, no different than 2 + 2.

Ternary Operator Replacement

For example, in Java, we sometimes use the “ternary operator”:

int foo = (bar > 10 ? 10 : bar)

Here, we are setting foo equal to bar, unless bar is over 10, in which case we cap foo at 10. The parenthetical expression returns what appears between the ? and the : if the boolean expression on the left is true, otherwise it returns what appears after the :.

Kotlin lacks ternary support, but more than makes up for it by allowing if to serve in the same role:

  val bar = 5
  val foo = if (bar > 10) 10 else bar

  println(foo)

This is the same business rule as in Java, just implemented using an if.

Our if can have full braces-wrapped code blocks, if we need them:

  val bar = 5
  val foo = if (bar > 10) {
    10
  }
  else {
    bar
  }

  println(foo)

The value of the last statement of the block is what that block evaluates to. In this case, each of those blocks is a single statement, but if we had several statements in a block, while they are all executed, the value from the last statement is used for the result (assuming that block is used, based on the conditional expression).

when Expressions

when can also be used as an expression:

  val i = 3

  val message = when (i) {
    in 3..10 -> "something"
    in 1..2 -> "something else"
    else -> "and now for something completely different"
  }

  println(message)

Here, the when evaluates to a string, which is assigned to message and printed.

Technically, the branches of the if or when do not have to evaluate to the same type. So, this runs:

val i = 3

val message = when (i) {
  in 3..10 -> "something"
  in 1..2 -> 5
  else -> "and now for something completely different"
}

println(message)

However, you will get some compiler warnings:

warning: conditional branch result of type String is implicitly cast to Any
  in 3..10 -> "something"
              ^
warning: conditional branch result of type Int is implicitly cast to Any
  in 1..2 -> 5
             ^
warning: conditional branch result of type String is implicitly cast to Any
  else -> "and now for something completely different"

Here, the compiler is telling you that since the branches evaluated to different types, the implied type of message is Any. Any in Kotlin is roughly analogous to Object in Java — it is the root type of the class hierarchy. Every class in Kotlin eventually extends from Any. Any is the only common ancestor class of Int and String, which is why the compiler chose that as the type for message. However, the warning is there to hint to you that perhaps what you wrote is not what you really have in mind.

Else Required

Normally, if and when do not have to be “exhaustive”. Here, “exhaustive” does not mean that it makes you tired (that would be “exhausting”). Rather, “exhaustive” means that all possibilities are covered by some branch. So, this is exhaustive:

if (i>10) {
  // do something
}
else {
  // do something else
}

Here, no matter what the value of i is, one of the two branches will be taken.

This, however, is not exhaustive:

if (i>10) {
  // do something
}

Nor is this:

val i = 3

when {
  i > 10 -> println("something")
  i > 2 -> println("something else")
}

In these cases, there are values for i for which none of the branches is valid. In that case, the if or when simply does not do anything.

For standalone if and when statements, this is fine. However, if you are going to use if and when as expressions, they must be exhaustive. After all, we are trying to evaluate the value of the if or when, and we do not have a value if there is some input for which no branch qualifies.

If you try a non-exhaustive if or when for an expression, such as:

val i = 3

val message = when (i) {
  in 3..10 -> "something"
  in 1..2 -> 5
}

println(message)

…you will get a compiler error:

error: 'when' expression must be exhaustive, add necessary 'else' branch
val message = when (i) {

Prev Table of Contents Next

This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.