Exploring Kotlin’s Nullability

I’ve often heard that one of the biggest advantages of using Kotlin is that you’ll never face NullPointerExceptions again. So I took a bit of time to explore what that means in detail.

The problem with with Java is that we often have to do null checks manually. A function such as the one below could easily cause a null pointer exception that we wouldn’t be able to discover until runtime.

int getHeight(View v) {
return v.height;
}

You could add the @Nullable annotation to let callers know what to expect

@Nullable
public
View getVisibleView() {
return someView;
}

But unfortunately, this would only give you a warning. While the warning is useful, it’s still pretty easy for other developers to ignore.

In Kotlin, the same function will return a compile time error instead, helping us avoid the dreaded run time exceptions.

fun getHeight(v: View) = v.height;
>>> getHeight(v)
Returns compile time error

If, however, you do want a function to be able to take in null arguments, you can do something such as :

fun getHeightSafe(v : View?)

The explicit question mark after View indicates that this variable can store a null reference. However, the problem then becomes that since this variable can be null, you can no longer call methods on it because that would cause a possible run time exception.

For example, the following would return an error:

>>> fun getHeightSafe(v : View?) = v.height;
ERROR: only safe(?.) or non-null asserted(!!.) calls are allowed ...
The code is complaining here because v might be null, and we didn't specify how we would want that handled

Thus, we would have to do the same kind of pattern that we usually do in Java code, where we check if a variable is null before calling methods on it.

// Java code
int getHeight(View v) {
if (v != null) {
return v.height;
} else {
return 0;
}
}

This becomes fairly verbose however, even if we were to do it in Kotlin. But luckily, there is the safe call operator: ‘?.’ that reduces this verbosity

// the following two methods are equivalent. In the first example, 
// we utilize the safe call operator to reduce verbosity
fun getHeightSafe(v : View?) : Int? = 
v?.height
fun getHeightSafe(v : View?) : Int = 
if (v !=null) v.height else 0

In the examples above if we passed in a null value we would return null. However, sometimes we want to pass back a default value instead of null. This is where the Elvis operator: ‘?:’ comes in. The Elvis operator allows us to provide default values.

fun getHeightSafe(v : View?) : Int? = 
v?.height ?: 0
// instead of passing back a default value, we can also throw an 
// exception when a null object is passed
fun getHeightSafe(v : View?) : Int? =
v?.height ?: throw IllegalArgumentException("Null view")
// you can explicitly throw the NullPointerException if the value is null with the !! operator
fun getHeightSafe(v : View?) : Int? =
v!!.height
>> getHeightSafe(null)
>> Exception in thread "main" kotlin.KotlinNullPointerException
...

Now our function will return 0 or throw an exception if we pass in a null value.

When working with Java and Kotlin together, the following rules apply:

@Nullable String in Java is equivalent to String? in Kotlin
@NotNull String in Java == String in Kotlin

There’s a lot of complexities and idiosyncrasies when working when nullables in Kotlin, and this article by Dan Lew walks through a lot of the http://blog.danlew.net/2017/06/14/convincing-the-kotlin-compiler-that-code-is-safe/