1. Overview
In this article, we’ll be looking at the null safety features built into the Kotlin language. Kotlin provides comprehensive, native handling of nullable fields – no additional libraries are needed.
2. Maven Dependency
To get started, you’ll need to add the kotlin-stdlib Maven dependency to your pom.xml:
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>1.1.1</version>
</dependency>
You can find the latest version on Maven Central.
3. Nullable and Non-Nullable Reference Types
Kotlin has two types of references that are interpreted by the compiler to give the programmer information about the correctness of a program at compile time – those that are nullable and those that are not.
By default, Kotlin assumes that value cannot be null:
var a: String = "value"
assertEquals(a.length, 5)
We cannot assign null to the reference a, and if you try to, it will cause a compiler error.
If we want to create a nullable reference, we need to create append the question mark(?) to the type definition:
var b: String? = "value"
After that, we can assign null to it:
b = null
When we want to access the b reference, we must handle the null case explicitly to avoid a compilation error because Kotlin knows that this variable can hold null:
if (b != null) {
println(b.length)
} else {
assertNull(b)
}
4. Safe Calls
Handling every nullable reference in such way could be cumbersome. Fortunately, Kotlin has a syntax for “safe calls” – this syntax allows programmers to execute an action only when the specific reference holds a non-null value.
Let’s define two data classes to illustrate this feature:
data class Person(val country: Country?)
data class Country(val code: String?)
Note that the country and code fields are of nullable reference type.
To access those fields in a fluent way, we can use the safe call syntax:
val p: Person? = Person(Country("ENG"))
val res = p?.country?.code
assertEquals(res, "ENG")
Should the variable p hold a null, the safe calls syntax will return a null result:
val p: Person? = Person(Country(null))
val res = p?.country?.code
assertNull(res)
4.1. The let() Method
To execute an action only when a reference holds a non-nullable value, we can use a let operator.
Let’s say that we have a list of values and there is also a null value in that list:
val firstName = "Tom"
val secondName = "Michael"
val names: List<String?> = listOf(firstName, null, secondName)
Next, we can execute an action on every non-nullable element of the names list by using a let function:
var res = listOf<String?>()
for (item in names) {
item?.let { res = res.plus(it) }
}
assertEquals(2, res.size)
assertTrue { res.contains(firstName) }
assertTrue { res.contains(secondName) }
4.2. The also() Method
If we want to apply some additional operation, for example logging on every non-nullable value we can use an also() method and chain it with a let():
var res = listOf<String?>()
for (item in names) {
item?.let { res = res.plus(it); it }
?.also{it -> println("non nullable value: $it")}
}
It will print out every element that is not null:
non nullable value: Tom
non nullable value: Michael
4.3. The run() Method
Kotlin has a run() method to execute some operation on a nullable reference. It is very similar to let() but inside of a function body, The Run() Method operates on this reference instead of a function parameter:
var res = listOf<String?>()
for (item in names) {
item?.run{res = res.plus(this)}
}
5. Elvis Operator
Sometimes, when we have a reference, we want to return some default value from the operation if the reference holds a null. To achieve that, we can use an elvis (?:) operator. This is an equivalent of orElse/orElseGet from Java Optional class:
val value: String? = null
val res = value?.length ?: -1
assertEquals(res, -1)
When the value reference holds a non-nullable value, the method length will be invoked:
val value: String? = "name"
val res = value?.length ?: -1
assertEquals(res, 4)
6. Nullable Unsafe Get
Kotlin also has an unsafe operator to get a value of a nullable field without handling absence logic explicitly, but it should be used very carefully.
The double exclamation mark operator (!!) takes a value from a nullable reference and throws a NullPointerException if it holds null. This is an equivalent of Optional.get() operation:
var b: String? = "value"
b = null
assertFailsWith<NullPointerException> {
b!!
}
If the nullable reference holds a non-nullable value, the action on that value will be executed successfully:
val b: String? = "value"
assertEquals(b!!.length, 5)
7. Filtering Null Values From a List
The List class in Kotlin has a utility method filterNotNull() that returns only non-nullable values from a list that holds nullable references:
val list: List<String?> = listOf("a", null, "b")
val res = list.filterNotNull()
assertEquals(res.size, 2)
assertTrue { res.contains("a") }
assertTrue { res.contains("b") }
This is a very useful construct that encapsulates the logic that we would otherwise need to implement ourselves.
8. Conclusion
In this article, we explored Koltin’s null safety features in depth. We saw types of references that can hold null values and those that cannot. We implemented fluent null handling logic by using “safe call” features and the elvis operator.
The implementation of all these examples and code snippets can be found in the GitHub project – this is a Maven project, so it should be easy to import and run as it is.