Kotlin – Nullability

All Java developers hate one simple thing about Java – NullPointerException. Worst nightmare caught on production during night-time. Having an instrument that allows to control null-types propagation through your codebase, would be beneficial for everyone. Would be even better if that could be done within compile-time.

Meet Kotlin nullability constraints.

Nullability constraints

Only an explicit variable can hold nulls and, in addition, we have compiler-time checks for passing nulls as method parameters. 
By default, a variable cannot accept null value.
There is a change in syntax to allow a variable to accept null. You will need to add a question mark at the end of variable type, e.g.:

        val a: String?

A nullable type cannot be assigned to the regular type. This case is also covered by the compile-time check.

Safe access operator

        m?.method()

This operator checks if the variable is null and does not execute a call to function if the check is true.

Elvis operator

        newMeeting = m ?: Meeting("important meeting")

Also known as a null-coalescing operator. It means: assign m to newMeeting if m is not null or otherwise create a new meeting and assign it to newMeeting.

Safe cast

        val saveable = o as? Saveable

Safely casts to Saveable or returns null, so return type will be Saveable?
The non-safe cast will drop exception:

        val saveable = o as Saveable

Not-null assertion

        m!!.close()

Checks that something is not null and throws NPE if it is. 
Good news: it will fail exactly at the line with a double exclamation mark and not somewhere later where this variable will be used the first time. Besides this construction should really make you think twice: is this really what you want to do at this place?

Let function

Used to avoid explicit null checks and it very useful when there is need to pass nullable type to non-nullable functions and we do not have access to modification of these functions (or we don’t really want to support nulls inside).

        fun functionNonNull(m: Meeting): Boolean? {
            return if (m.canClose) m.close() else false
        }

        val m: Meeting? = null

        m?.let {
            functionNonNull(m)
        }

In this case, functionNonNull will be called only when m is non-null.

Late initialized properties and lateinit construct

Sometimes values cannot be initialized when declared. At this point you have options:

  • mark them as nullable (can be that you don’t really want that, like in case of Spring cause you definitely know that as run-time those variables won’t be null) but this is a code-smell;
  • Mark them as lateinit variables. This is compiler keyword and basically what it means is: I declare this variable, I know that it’s null right now but I promise it will be initialised before the first use. This allows to make your code more concise, predictable and readable.

Kotlin nullability interoperability with Java Code

Kotlin understands @Nullable and @NotNull annotations from Java world. Also, Kotlin has support for annotation from javax.annotation package and org.jetbrains.annotation package.

Java-Kotlin interoperability table can be found on below.

JavaKotlinDescription for Kotlin
@Nullable TT?  Regular nullable type
@NotNull TTNon-nullable type
T Platform type Platform type. Can be nullable and non-nullable

Platform type basically means that at the point of variable usage we have no information out from javax annotations if this variable is nullable or not.

You decide how to treat platform type on your own: as nullable or non-nullable type.