Kotlin Nullable Types vs. Java Optional

When Java 8 introduced the Optional type many years ago, I was happy like a bird. I already knew about the Optional type from various adventures into functional programming languages and knew of its powers.

When I learned about Kotlin last year, it seemed to have some similarities to Scala, making me very excited. Initially I was surprised that Kotlin did not embrace the Optional type like Scala did. Of course, there was a very good reason. Kotlin natively supports nullable types, making the Optional type, as well as all the API it provides, obsolete.

In this post we will see how the Optional type compares and translates to nullable types in Kotlin. Prior knowledge about both Optional and nullable types is assumed.

Optional/Nullable Properties and Return Types

In Java, an Optional property can be declared as follows:

public interface Person {
String getName();
Optional<Integer> getAge();

Instead of using the Optional type, in Kotlin we will just declare the age property as nullable:

interface Person {
val name: String
val age: Int?

Optional.map() vs. Safe-Call Operator

public interface Car {
Person getDriver();
Optional<Car> car = getNextCarIfPresent();

Optional<Integer> driversAge =

This code snippet retrieves the driver from an Optional car and then retrieves that driver’s optional age. Since both, the car and the age are optional, we are forced to use the flatMap() method when retrieving the driver’s age. In Kotlin we can use the built-in safe-call operator ?. to access properties of nullable types:

interface Car {
val driver: Person
val car: Car? = getNextCarIfPresent()

driversAge: Int? = car?.driver?.age

Optional.map() vs. let() Function

Optional<DriversLicence> driversLicence =

In Kotlin we will have to use the stdlib’s let() function to invoke external functions within a chain of safe-call operators to achieve the same goal.

val driversLicence: DriversLicence? = car?.driver?.let {

Optional.orElse() vs. Elvis Operator

boolean isOfLegalAge =
car.map(Car::getDriver).flatMap(Person::getAge).orElse(0) > 18;

When the chain of map calls returns a non-empty age, it will be used. Otherwise the provided fallback value 0 will be used. Kotlin has the built-in elvis operator ?: for this purpose:

val isOfLegalAge: Boolean = car?.driver?.age ?: 0 > 18

Optional.get() vs. Assertion Operator

boolean isOfLegalAge =
car.map(Car::getDriver).flatMap(Person::getAge).get() > 18;

In Kotlin we can simply use the built-in assertion operator !! for this:

val isOfLegalAge: Boolean = car?.driver?.age!! > 18

Optional.filter() vs. takeIf() Function

Optional<Person> illegalDriver =
car.map(Car::getDriver).filter(p -> p.getAge().orElse(0) < 18);

In Kotlin we use the stdlib’s takeIf() function and end up with much less code:

val illegalDriver: Person? = car?.driver?.takeIf { it.age ?: 0 < 18}

Optional.ifPresent() vs. let() Function

.filter(person -> person.getAge().orElse(0) < 18)
.ifPresent(illegalDriver -> {

In Kotlin we would again use the let() function for this:

car?.driver?.takeIf { it.age ?: 0 < 18 }?.let { illegalDriver ->


Besides the more readable code, Kotlin’s nullable types have several advantages over Java’s Optional type.

First, there is no runtime overhead involved when using nullable types in Kotlin¹. Unlike Optional, no wrapper object is created for wrapping the actual value.

Second, nullable types in Kotlin provide null-safety at compile-time. Using them in a non-safe way will lead to a compile-time error. In contrast, using the Optional type in a non-safe way is not checked by the compiler, and will lead to a runtime exception.

¹ unless we are dealing with nullable primitive types, in which case the boxed version of the primitive type is used on JVM level.



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store