Idiomatic Kotlin: Elvis Operator

Tompee Balauag
Familiar Android
Published in
3 min readJul 13, 2018
“The interior of an office with old furniture.” by Joshua Ness on Unsplash

This article is a part of the Idiomatic Kotlin series. The complete list is at the bottom of the article.

In today’s article, we will talk about writing Elvis operator and how it can make our code rock (and roll).

What is the Elvis operator?

The Elvis operator in Kotlin is an operator that receives two inputs and returns the first argument if it is non-null or the second one otherwise. It is fancily called the null-coalescing operator. It is a variant of the ternary operator but for null-safety checking.

Motivation

Kotlin, as you may know, supports nullability explicitly. This is an attempt to minimize the dreaded NullPointerException during runtime and catch them all at compile time. If you are writing pure Kotlin code, then chances are you may not be going to deal with nullable types. But the Java JDK, Android framework and other Java libraries will force you to deal with them. Let us look at how verbose it is to check for nullability in Java.

In Java, reference types can always be null. Therefore, you have to check them for nullability everytime to make sure that you will not encounter NullPointerException at runtime. This is equivalent to 3 lines minimum of code for every instance. And trust me, this can easily get out of hand in nested instance cases.

Kotlin introduced a lot of operators to make nullability checks idiomatic, and easier to read. One of these operator is the Elvis operator. We are singling out the Elvis operator because I think it is one of the most underused feature (I, too, am guilty as well) in the collection of Kotlin nullability check tools.

How to use the Elvis operator?

The syntax of the Elvis operator is ?:. A simple example would be:

Wait a minute. I thought it is a variant of the ternary operator. Then why am I seeing two inputs only? As we have defined above, the main purpose is for nullability checking, so the null check is implicit. The expanded operation will look something like this

One of the main strengths of Elvis operator is its use in conjunction with the safe call operator ?. As a background, you can access a nullable objects’ properties or methods by simply using the safe call operator. However, using the safe call operator will imply that the resulting type will be nullable as well. Let me show you what I mean through an example.

Notice that the age is now a nullable type. This is because the john instance is a nullable Person. Even though you are sure that the john instance is not null, the compiler cannot assume because it’s type is nullable. Therefore, the safe call operator here only provides an easy syntax when checking and accessing the age property of the Person class but the output would still be of nullable type. Elvis operator provides options in avoiding propagation of nullable types.

First option is to provide default non-null value.

Now we can compile the same function successfully. This is because we made sure that a default non-null value will be assigned to age in case the john instance is null and the compiler inferred it correctly.

Another option is to throw error. The beauty of the Elvis operator is that the second argument can be an expression, not just a variable.

The compiler is still smart enough to infer that the age is non-nullable because if in case it was, an exception will be thrown.

Elvis operator under the hood

Let us try to decompile this code below and see how it translates to Java code.

The decompiled version is below

No magic here guys. It still performs a series of null checking down to the deepest child. We can therefore say that there is no added overhead, and no performance improvement either.

Check out the other articles in the idiomatic kotlin series. The sample source code for each article can be found here in Github.

  1. Extension Functions
  2. Sealed Classes
  3. Infix Functions
  4. Class Delegation
  5. Local functions
  6. Object and Singleton
  7. Sequences
  8. Lambdas and SAM constructors
  9. Lambdas with Receiver and DSL
  10. Elvis operator
  11. Property Delegates and Lazy
  12. Higher-order functions and Function Types
  13. Inline functions
  14. Lambdas and Control Flows
  15. Reified Parameters
  16. Noinline and Crossinline
  17. Variance
  18. Annotations and Reflection
  19. Annotation Processor and Code Generation

--

--