Lesson 6 - Type system: Null safety in Kotlin
In the previous exercise, Solved tasks for Kotlin lesson 5, we've practiced our knowledge from previous lessons.
Lesson highlights
Are you looking for a quick reference on
null
safety in Kotlin instead of a thorough-full lesson? Here it
is:
Declaring and printing a nullable variable:
{KOTLIN_CONSOLE}
var maybeNumber: Int? = null
println(maybeNumber)
{/KOTLIN_CONSOLE}
Working with a nullable variable safely using a condition:
{KOTLIN_CONSOLE}
var maybeNumber: Int? = 15
if (maybeNumber != null) // the condition allows to perform * 2 below
println(maybeNumber * 2)
else
println("No value to work with")
{/KOTLIN_CONSOLE}
Alternative null
condition
using ?.let
:
{KOTLIN_CONSOLE}
var maybeNumber: Int? = 15
maybeNumber?.let { println(maybeNumber * 2) } // won't be called if null
{/KOTLIN_CONSOLE}
Forcing the value of a nullable variable using the
!!
operator (unsafe):
{KOTLIN_CONSOLE}
var maybeNumber: Int? = 15 // if this is null, the program crashes
println(maybeNumber!! * 2)
{/KOTLIN_CONSOLE}
Safe calling:
{KOTLIN_CONSOLE}
var name: String? = null
println(name?.length) // prints null, doesn't crash
{/KOTLIN_CONSOLE}
Using the Elvis operator
?:
:
{KOTLIN_CONSOLE}
var greeting: String? = "Hello world"
println(greeting?.length ?: 0) // 0 is used when greeting is null
{/KOTLIN_CONSOLE}
Would you like to learn more? A complete lesson on this topic follows.
Are you looking for a quick reference on creating a Java SE project in the NetBeans IDE instead of a thorough-full lesson? Here it is:
In the previous lesson, Solved tasks for Kotlin lesson 5, we learned how to write less code using loops. Now we're going to finally explain the concept of null safety, which is very important in Kotlin. We'll talk about what all the exclamation marks in source code mean. We've already stumbled across a few, and although it may be a little complex concept to start with, it's better to understand at least a little bit what it's all about.
The null
value concept
Programming languages must somehow deal with a situation where a variable has
no value. We often encounter this problem with functions which hasn't been
executed properly. For example, if a number couldn't be retrieved from the
console, the output shouldn't be a number but rather an "empty" value. If Kotlin
returned the 0
or -1
value in this case, we wouldn't
be sure whether the number indicates it wasn't possible to read it or the user
had entered 0
or -1
. For this purpose, a special
null
value was introduced, which safely indicates that a variable
is empty.
If we create a standard variable in Kotlin, we can't assign the
null
value to it:
// This code won't work var number = 15 number = null // This line will cause an error
That's because someone may not be expecting null
there. In order
to assign null
to a variable, we must declare the variable as
nullable first.
Nullable types
A nullable type can be seen as kind of a box that wraps an ordinary variable.
The box always exists, but when opened, the value is either there or
it's empty. To create a nullable type, we place a question
mark ?
after the data type of the variable. Let's try
it:
var maybeNumber: Int? = 15
maybeNumber = null
The code was compiled successfully and the maybeNumber
variable
is now empty, even though it's a number. That sounds good, right?
There's however a problem that a lot of other programming languages failed to
deal with. We shouldn't be able to work with the
maybeNumber
variable as with a normal variable. If we
wrote:
var maybeNumber: Int? = 15 maybeNumber = null println(maybeNumber * 2)
and the program would be compiled, it could cause a runtime crash in the case
of maybeNumber
being empty. Of course, we can't multiply "empty" by
two. If we tried to write such a program, you'd find that it won't
compile. Similarly, Kotlin wouldn't let us access a method or property
of a nullable type. You can try yourself that the following code won't compile
because of printing the second string's length:
var s1 = "Hello" var s2: String? = "World" println(s1.length) println(s2.length)
You've probably guessed that Kotlin doesn't belong to languages that can't deal with this problem
Null safety
The mechanism checking how we use nullable type during the compilation is called null safety. There are several ways to use a nullable variable. Let's try them out one by one.
The !!
operator
Let's start with the most silly one which we've been already using in this
course, so that the amount of information wasn't that overwhelming. Using the
!!
operator we can degrade Kotlin to the level of older languages
such as e.g. Java and bypass the null safety
checking. Everything will work unless the variable is
null
:
var maybeNumber: Int? = 15 println(maybeNumber!! * 2)
The output:
30
If it's empty though, the whole application will crash:
var maybeNumber: Int? = 15 maybeNumber = null println(maybeNumber!! * 2)
Since we wouldn't even notice this mistake during the compilation, we won't
be using the solution with !!
much.
Conditions
A bit smarter solution is to work with nullable types in a condition checking
the null
value. Since we can avoid application crashes like this,
Kotlin will allow us to compile the program:
var maybeNumber: Int? = 15 if (maybeNumber != null) println(maybeNumber * 2) else println("The entered value isn't a number")
Safe calling
You surely guessed that there are better solutions then writing
null
conditions over and over. Using the ?.
operator
(question mark, dot) we'll either get the given property or null
will be returned if the variable is empty.
?.let
If we used safe calling together with the let
keyword, the code
in the braces would be executed only if the variable contains a non-null
value:
var maybeNumber: Int? = 15
maybeNumber?.let { println(it) }
The it
keyword in the block contains the value. If
maybeNumber
was null
, the program would compile and
the printing wouldn't be executed.
The ?.
chaining
We only use this functionality if we want to ask through a chain of properties, for example:
student?.teacher?.superviser?.name
The expression above will either return the name of the school principal (the
supervisor of the student's teacher) or null
if any expression part
is empty. We save a lot of conditions like this, but we have to remember that we
still end with a nullable type, even it's just one.
We could safely assign like this as well without the need for additional conditions:
student?.teacher?.supervisor?.name = "Seymour Skinner"
The Elvis operator
I don't think it's necessary to explain the name origins here We use Elvis along with the
?.
operator. It let us ask whether the value in a nullable variable
is null
and use a default value instead. Again, let's try it on our
example:
var maybeNumber: String? = "Hello world" println(maybeNumber?.length ?: 0)
On the right side of the Elvis operator, we can also use return
or invoke exceptions (see further courses).
For each way of handling the null
values, when the
value is null
, other expressions (methods) meant for the non-null
scenario, won't be executed.
In the next lesson, Arrays in Kotlin, we'll talk about arrays.