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:
- score > ‘80’
- 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
andelse 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
.