Utilize Control Flow in Kotlin

May 22 2024 · Kotlin 1.9.23, Android 14, Kotlin Playground

Lesson 01: Branch Control Flow

Demo

Episode complete

Play next episode

Next
Transcript

Open the starter project and look at the main() function. Above the code editor, make sure the Kotlin version is set to 1.9.23 or later and the platform is set to JVM.

The program reads the input from the main function arguments. Set the arguments in the playground website using the input field above the code editor. The args argument is an array of strings. For example, if you set the input to A B+, the args array will contain two elements: A and B+. If you pass no arguments, the args array will be empty.

In all the examples of this lesson, only the first arguments are meaningful. Access it using the firstOrNull() function. It returns the first element of the array — or null if the array is empty.

The program expects the first argument to contain the grade. And, for the simplicity of the examples, the program supports only the whole grades. We can ignore any suffixes like + or -. That’s why thereis a second firstOrNull() call. It gets the first character of the first argument. The latter is nullable - it can be null itself if no arguments are given. That’s why we also need to use the safe call (?.) operator. You’ll learn more about nullability and the safe calls soon.

The grade variable is a Char type. It will contain only the first character of the input or null. It will be null if no arguments are given or if the first one is an empty text. Next, we have the messagePrefix variable to use in the output inside the conditions you’ll write soon.

The first condition checks if the grade is equal to A. If it is, the program prints the message with that fact. Add the following code to the main() function by replacing theTODO comment:

    if (grade == 'A') {
        println("$messagePrefix excellent")
    } 

The syntax of the if statement consists of the condition expression inside the parentheses and the body inside the curly braces. The body is executed only when the condition is met. Or, in other words, if the expression evaluates to true.

Inside the curly braces, you can put any code you want. The program will execute all that code if the condition is met. In this case, the program contains only one line of code — one statement. It prints the message about the excellent grade to the standard output.

You can write more lines — more statements — inside the curly braces. For example, you can add one more print statement to the body of the if statement:

    if (grade == 'A') {
        println("$messagePrefix excellent")
        println("You are the best")
    }

Notice the program prints both messages. The curly braces are optional. Without them, only the first line after the condition is considered as the body. But, using them makes the code more readable, and readability is important for code maintenance. In the real project, you’ll spend a lot more time reading the code than writing it.

The code still works, but it’s less readable.

At this time, you have only one condition. If the grade isn’t equal to A, nothing happens. To add more conditions, use the else if statement like this:

    if (grade == 'A') {
        println("$messagePrefix excellent")
    } else if (grade == 'B') {
        println("$messagePrefix very good")
    } 

If you run the code with B as input, the output is Your grade is very good as it is in the else if branch.

Add as many else if statements as you want. Cover all the possible grades, from A to F:

    if (grade == 'A') {
        println("$messagePrefix excellent")
    } else if (grade == 'B') {
        println("$messagePrefix very good")
    } else if (grade == 'C') {
        println("$messagePrefix good")
    } else if (grade == 'D') {
        println("$messagePrefix acceptable")
    } else if (grade == 'F') {
        println("You failed")
    }

You don’t have to use the same pattern for each message. For the F in the input the output is You failed. There is no messagePrefix used in this case.

Note: This is important! The more branches there are, the less readable the code becomes. In real-world applications, avoid long lists of conditional branches.

There are other ways to handle large numbers of cases in Kotlin. You’ll learn about them in the next lesson.

So far, so good. But, what if the grade is not one of the letters? The user may make a typo or try to input a grade like Z. The program won’t print anything in that case, so it’s a good practice to tell the user the input is invalid. To handle those cases, add the else statement:

    if (grade == 'A') {
        println("$messagePrefix excellent")
    } else if (grade == 'B') {
        println("$messagePrefix very good")
    } else if (grade == 'C') {
        println("$messagePrefix good")
    } else if (grade == 'D') {
        println("$messagePrefix acceptable")
    } else if (grade == 'F') {
        println("You failed")
    } else {
        System.err.println("Unrecognized grade: $grade. Valid grades are A, B, C, D, and F.")
    }

Now, the program handles the invalid input. It prints the message about the unrecognized grade. The error message appears red in the console — unlike the regular messages, which are white. That’s due to the System.err.println function instead of plain println.

Always distinguish the messages with data, like You are good from the information about errors, like Unrecognized grade: Z. Imagine that a program writes not to the console but to the file like a spreadsheet (CSV or XLSX). The error message must not land in the spreadsheet but be presented to the operator running your program.

Note: Never trust the input from the user or any other source outside your control, like the server response. Always handle the case when the input is not what you expect.

In Kotlin, scopes are defined by pair of curly braces. For example, the messagePrefix variable is scoped to the main function. It’s visible only here, but not outside. Shadow it in scope of one of the branches. For example, change the message prefix for the B grade:

    } else if (grade == 'B') {
        val messagePrefix = "You are"
        println("$messagePrefix very good")
    } 

In this case, the messagePrefix for a B grade becomes You are instead of Your grade is. In the real apps, use shadowing wisely. It may make the code less readable and more error-prone.

You may ask what happens if you try to use the variable outside of its scope. To check that, modify the end of the code like this:

    } else {
        val message = "Unrecognized grade: $grade. Valid grades are A, B, C, D, and F."
    }
    println(message)

The code won’t compile. The message variable isn’t visible outside of the else branch.

What if the conditions of two or more branches are met at the same time? For instance, consider the following conditions:

  1. score > ‘80’
  2. score > ‘90’

The code may look like this:

    val score = args.firstOrNull()?.toIntOrNull() ?: 0
    if (score > 80) {
        println("Grade B")
    } else if (score > 90) {
        println("Grade A")
    }

There’s a fallback to 0 for invalid input before the if statement. Without it, the code won’t compile. That’s because to check if score is greater than something else, it has to be non-nullable. You’ll learn about null values and nullability soon.

Let’s say the input is 95. The output will be Grade B.

It may be surprising. That’s because the first condition is met. 95 is greater than 80. The next condition is also met because 95 is also greater than 90. But, the second condition is not evaluated by the program as the first condition itself is met!

Note: In case of the if and else if statements, the first matching condition wins. The order of the conditions matters.

Change the order of the conditions to the following:

    val score = args.firstOrNull()?.toIntOrNull() ?: 0
    if (score > 90) {
        println("Grade A")
    } else if (score > 80) {
        println("Grade B")
    }

Program will print Grade A for input 95.

See forum comments
Cinema mode Download course materials from Github
Previous: Instruction Next: Conclusion