Null Safety Tutorial in Kotlin: Best Practices

In this tutorial, you’ll look at Kotlin’s nullability best practices. You’ll learn about null safety in Kotlin and how to avoid NPEs. By Kolin Stürt.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 4 of 4 of this article. Click here to view the first page.

Using Null in Collections

Kotlin also improves safety by disallowing nullable variables in collections by default, but there are times when you might want to allow them. Some examples are interoperability with legacy code or a circular buffer that is initially set to all nulls.

Navigate to the legacy folder in the project and open the FileShredder.java file. Inside the stringForMode method, you’ll notice that it will return null for modes that are unknown. It does that by design, but Kotlin doesn’t really know that.

To test how it works, create a test directory on your device and try to delete it:

Delete a file

Null cast exception

That happens because there isn’t a valid mode for folders, so item is null.

Let’s say you can’t change the legacy code. Instead, in FileHelpers.kt, replace the wipeFile method definition with the following:

private fun wipeFile(file: File, operations: List<String?>) {

You’ve just told Kotlin that the List can include nulls. At this point, you could start introducing safe calls around the code, but since there isn’t any logic associated with null states, you can simply filter them out.

Replace the line right before the for loop with this code:

val validOperations = operations.filterNotNull()
val iterator = validOperations.listIterator()

Here, you called filterNotNull() to remove nulls from the List. This also works well if you are using helper methods such as String::toDoubleOrNull().

Build and run the app. Go ahead and delete the test directory that you created. This time, you should succeed!

Deleted successfully

Happy face

Interoperating With Other Languages

You’ve now gotten through all the best practices for nullability in Kotlin.

However, although Kotlin is safer than Java, you won’t always work with a pure Kotlin app. There will often be legacy code that’s too expensive to change, and some teams simply prefer Java.

Nullability in Java

There are no null safety checks for types you declare in Java. As you’ve seen with the legacy code you just worked with, types coming from Java subvert the checking system!

There’s a good chance that you expect an object in Kotlin, but from Java it’s null.

Note: NPEs from Java are still security issues. When the app crashes, it gives valuable insight into the internal logic of the executing code block. This could potentially enable someone with enough interest to exploit the app. i.e You forgot to check user input, the app crashes on no input value(null) and generates a stacktrace. This stacktrace usually consists of the call stack, which in turn allows hackers to deduce how to feed a valid albeit fake value to the app and fool it.

The best practice is to treat all variables coming from Java as nullable in your Kotlin code. If you can change the Java code, a better solution is to update it to include nullability annotations.

Annotations don’t alter any existing logic, but instead tell the Kotlin compiler about nullability. The two important annotations are @Nullable and @NotNull.

Nullability in C++

For games and for code that is performance-sensitive or portable, it’s common to use C++ as the preferred language.

C++ is powerful because it allows you to work with memory pointers. Here are a few points about pointers:

  • As with references, you can set a pointer to null.
  • As soon as you bring native code into the mix, you introduce a whole set of memory, safety and security issues surrounding null dereferences. Pointers allow you to access raw memory locations, making it easier to read and write to any memor area. This counts as one of the major ways for attackers to maliciously change a program.
  • C++ doesn’t offer nullability annotations like Java does. Instead, document your functions well by stating whether the parameters and return values can be null or not.
  • In normal cases, you’ll set a pointer to null when you are finished with it, and not store or return pointers for later use. That allows you to work with the pointer only while it’s valid.
  • The true native meaning of null is actually a zero. Zero was late to the party in computational systems, arriving only after 5,000BC. It was null before that. :]
  • You can find more info about how Kotlin works with pointers by checking out the Pointer types section of the Kotlin reference, as well as the CPointer and COpaquePointer objects.

Where to Go From Here

You’ve just completed a crash course in null safety!

Download the final project using the Download Materials button at the top or bottom of this tutorial. Armed with your new knowledge, you can safely embark onto the journey into the void.

Finding a crash is like holding a mirror up to yourself. It provides you valuable insight into which common mistakes you make as a developer and how you can improve. Instead of waiting for your users to find them, testing your code will go a long way. There’s a lot of info available on Android’s page about testing.

If you have any questions about nullability, or perhaps thoughts about the meaning of nothing, please join the discussion below!