Kotlin when expression
Kotlin when expression

In this tutorial, you will learn about Kotlin when{} block use cases. If you are just learning Kotlin or want to refresh your knowledge, this information will be useful for you.

Kotlin when expression

In this tutorial you will learn about Kotlin when{} block use cases. If you are just learning Kotlin or want to refresh your knowledge, this information will be useful for you.

when{} Block in Kotlin

Kotlin when is a more advanced version of the switch-case statement in Java. The main advantage of when{} block is that if a matching case is found, only the code in that particular case block is executed, and the program execution continues with the next statement after when block. Therefore, there is no need to use break statements at the end of each case block.

To better understand the usage of when{}, we can define an enum class that contains the first letter in the permissions field for some file types in Unix. Let’s find out more about Kotlin when syntax and the use cases of this block.

“When{}” as an Expression: Understanding Kotlin’s Flow-Control Structures

The Kotlin “when{}” block differs from the switch statement in Java by its powerful flow control structure. Also, the when{} block can be used as both a statement and an expression. This function is consistent with the principles of other functional programming languages where flow control structures are expressions and can be evaluated to return a value to the caller.

fun printType(obj: Any) {
    val typeString = when(obj) {
        is String - > "String"
        is Int - > "Integer"
        is Double - > "Double"
        else - > "Unknown"
    }
    println("$obj is of type $typeString")
}

In this example, we create a printType function that takes an Any object and prints its type to the console. The flexibility when in Kotlin means that it can be used in various scenarios, such as checking different cases and executing corresponding code blocks, as well as performing complex operations based on input values. Moreover, “when{}” expressions can be nested, allowing for even more intricate control flow structures.

When{} as an Expression With Default Case

Kotlin uses the else construct by default to handle all arguments that don’t match the normal conditions. The Kotlin compiler assumes that all possible argument values are handled in when block and throws an error if they are not.

To add a default case in Kotlin’s when expression, simply include the else clause at the end. This will ensure that any unmatched argument value is handled appropriately. It is also possible to add additional logic or statements within the else clause to handle the default case in a specific way.

Overall, the default case in Kotlin’s when expression provides a useful mechanism for handling unmatched argument values and ensuring that all possible scenarios are accounted for. By following these guidelines, you can effectively implement default cases in your Kotlin code and improve its robustness and reliability.

fun getColorCode(color: String): String {
    return when(color) {
        "red" - > "#FF0000"
        "green" - > "#00FF00"
        "blue" - > "#0000FF"
        else - > "#000000"
    }
}

The color code is returned in hexadecimal format when we define the getColorCode function. An example of a function call:

val colorCode = getColorCode("green")
println("The color code is $colorCode")

Expression With a Case That Throws an Exception

In this case, the “throw” operator generates the value “Nothing”. It is used to indicate that the expression cannot evaluate a value. Interestingly, ‘Nothing’ is a special type in Kotlin that is the parent of all built-in and custom types.

Consequently, because ‘Nothing’ is compatible with any parameter employed in a ‘when’ block, we can throw an exception from a case without any issue, even when ‘when’ is being used as an expression.

fun checkNumber(number: Int) {
    when(number) { in 1. .10 - > println("The number is between 1 and 10") in 11. .20 - > println("The number is between 11 and 20")
        else - >
            throw IllegalArgumentException("Invalid number: $number")
    }
}
fun main() {
    checkNumber(5)
    checkNumber(15)
    checkNumber(25)
}

In this example, we use the toIntOrNull() function to convert a string to a number. We then check the parsedInput value with the when construct. If parsedInput is null, we throw a NumberFormatException. If parsedInput is not null, we return its value.

When{} Used as a Statement

How to use a when block as a statement? Besides using when statement kotlin as an expression, we can also use it as a statement. In this case, we don’t need to cover every possible value of the argument, and the value evaluated in each case block is ignored. As a statement, we can use a when block similarly to how we use a switch statement in the Java language. 

Let’s consider when block as a statement:

fun main() {
    val number = 2
    when(number) {
        1 - > println("Number is one")
        2 - > {
            println("Number is two")
            println("It's a great number")
        }
        3, 4 - > println("Number is three or four")
        else - > println("Number is not between 1 and 4")
    }
}

When using when statement as a conditional statement, it is not necessary to describe all possible argument values.

Combining when{} cases

when expression in Kotlin is used to combine different cases into one by concatenating match conditions with a comma.

Only one case is sufficient to execute the corresponding block of code, so the comma acts as a logical OR operator.

Let’s create a case that combines two conditions:

val x = 5
when(x) {
    1 - > println("One")
    2, 3 - > println("Two or Three")
    else - > println("Other")
}

In this example, we have combined two matching conditions (3 and 4) into a single block by separating them with a comma.

When{} Used Without an Argument

By omitting the argument value, when{} effectively becomes an if-elseif expression. It sequentially evaluates the cases until it finds a matching one, and then executes the corresponding code block. In this case, the case expressions must evaluate as either true or false.

To illustrate this feature, let’s take a look at an example of a when{} block without an argument:

val x = 2

when {
    x == 1 - > println("x is 1")
    x == 2 - > println("x is 2")
    x == 3 - > println("x is 3")
    else - > println("x is not 1, 2, or 3")
}

Dynamic Case Expressions

Java only permits the switch statement to be used with primitives, their boxed types, enums, and the String class. In contrast, Kotlin provides the flexibility to use when block with any built-in or user-defined type. Unlike Java, the cases in Kotlin’s when block do not need to be constant expressions. They can be dynamic expressions that are evaluated at runtime. This means that cases in Kotlin can be determined by a function as long as the function’s return type is compatible with the type of when block argument. To illustrate, we can create a when block with dynamic case expressions as follows:

fun main() {
    val input: Any = 5

    when(input) {
        is String - > println("Input is a String") in 1. .10 - > println("Input is between 1 and 10")
        15, 20 - > println("Input is either 15 or 20") !is Int - > println("Input is not an integer")
        else - > println("Input is something else")
    }
}

Range and Collection Case Expressions

Kotlin offers the convenience of defining a condition within a when block to determine whether a specified collection or a range of values contains a given argument.

To accomplish this, the in operator is available in Kotlin, which serves as a shorthand for the contains() method. In other words, Kotlin internally translates the in operator to collection.contains(element) to perform the comparison.

fun main() {
    val num = 10
    when(num) { in 1. .5 - > println("Number is between 1 and 5") in listOf(7, 9, 11) - > println("Number is either 7, 9 or 11") ! in 0. .20 - > println("Number is outside the range of 0 to 20")
        else - > println("Number is none of the above")
    }
}

When is Case Operator and Smart Cast

It is possible to use Kotlin’s operator to check if an argument is an instance of a particular type. The operator is similar to the instance of the operator in Java.

However, the smart cast is available in Kotlin. After checking whether an argument is an instance of a given type, we don’t need to explicitly cast the argument to that type, since the compiler does it for us.

Thus, we can use the methods and properties defined on the given type directly in the case block.

Let’s use the is a statement along with the smart cast function in when block:

fun printLength(obj: Any) {
    when(obj) {
        is String - > println("String length: ${obj.length}")
        is Int - > println("Number length: ${obj.toString().length}")
        else - > println("This object is not a string or a number.")
    }
}

Conclusion

As you can see, when in Kotlin can be used to replace if-else if in some cases. But one of the main advantages of using when is the ability to use smart casts that automatically cast a variable to the correct type.