July 27, 2018

Optional in Java

Optional type was introduced to denote absence of value in API.

Reasons behind Optional in Java

Speaking at a software conference called QCon London in 2009, Tony Hoare apologised for inventing the null reference:

I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language. My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn’t resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.

In Java API documentation Optional is described as:

A container object which may or may not contain a non-null value.

Use Cases

Denote absence of value in API

There are no use cases, there is the only one use case! As Vitaly Davidovich states:

Optional is being added for the value it offers in “fluent” sequences of statements.

Optional is not supposed to solve null reference problems. It is supposed to denote that value is optional - it could exist or could not exist.

Usage of Optional type in API significantly improves readability and could prevent errors which could be caused if null is used to denote absence of value.

Method with signature:

public Comission comissionFor(Customer customer);

Does not tell how to determine that might be a situation without commission for a customer and even if method returns null to determine absence of commission. How developer could be sure that this is not an error?

And on the other hand:

public Optional<Comission> comissionFor(Customer customer);

Directly says that there is a potential situation without commission and if method returns null, there is a bug in the code.

So, if there is need to denote absence of value and null is used to solve this issue, Optional is more preferred way to do it.

Disadvantages

Performance

The Optional type is additional wrapper and requires memory to be allocated for it. And in case if you have a lot of objects wrapped into Optional and high performance is requirement, it could be a real problem.

In future versions (or at reading time it maybe current version) value types could remove this drawback.

Serialization

Optional type is not Serializable, because it was not supposed to be used in fields. By design, Optional is supposed to be used as return type.

Meaningless types

In case of overuse of Optional, things like:

Map<Integer, Optional<List<Optional<Integer>>>>

Could be very hard to understand. But this problem could be solved by providing meaningful types, which could be closer to domain.

Debugging

Wrapping objects into Optional also could lead to larger call stack, which also complicates debugging.

Still throwing exceptions

The Optional type still could throw exception if not used properly. Replacing all references to Optional objects would not help to avoid exceptions. API should be accurately designed to denote potential absence of values. And requirement to work carefully with such API is left.

In case if it is used just to remove null checks, it is better to consider tools like Nullness Checker. And, by the way, this tool perfectly complements Optional type.

Optional API

Optional.of, Optional.ofNullable and Optional.empty

In short:

  1. Methods of and empty could serve best in new code for constructing Optional values.
  2. Method ofNullable could be used as wrapper for old API which uses null value to determine absence of value.

The constructor of Optional is private and method of serves as static factory for creating Optional values:

Optional<BigDecimal> money = Optional.of(BigDecimal.valueOf(10000));

System.out.println(money);
// Optional[10000]

It will throw exception in case if null is passed:

Optional.of(null);
// Exception in thread "main" java.lang.NullPointerException

There is a dedicated method for handling null values and converting them to “empty” Optional objects - ofNullable:

BigDecimal nullableMoney = null;
Optional<BigDecimal> optionalMoney  = Optional.ofNullable(nullableMoney);

System.out.println(optionalMoney);
// Optional.empty

In case if there is need for empty Optional object which determines that there is no value:

Optional<BigDecimal> optionalMoney  = Optional.empty();

System.out.println(optionalMoney);
// Optional.empty

Optional.isPresent, Optional.ifPresent and Optional.ifPresentOrElse

Methods isPresent, ifPresent and ifPresentOrElse are imperative relatives. E.g. method isPresent is usable in pair with get. It is a common pattern in “imperative” style, to test if value present and get it. Or method ifPresent could be used to perform operation with side-effect if there is value.

So, isPresent tests if value is present:

Optional<BigDecimal> optionalEmpty  = Optional.empty();
System.out.println(optionalEmpty.isPresent());
// false

Optional<BigDecimal> optionalFromNull  = Optional.ofNullable(null);
System.out.println(optionalFromNull.isPresent());
// false

Optional<BigDecimal> optionalWithValue = Optional.of(BigDecimal.TEN);
System.out.println(optionalWithValue.isPresent());
// true

Also an operation could be performed if value is present:

Optional<BigDecimal> optionalWithValue = Optional.of(BigDecimal.TEN);

optionalWithValue.ifPresent((x) -> System.out.println(x.add(BigDecimal.TEN)));
// 20

Method ifPresent does not return a value, it just performs operation if value is present. In case if there is need to act if there is no value, ifPresentOrElse is more suitable:

Optional<BigDecimal> optionalWithValue = Optional.of(BigDecimal.TEN);
optionalWithValue.ifPresentOrElse(
        (v) -> System.out.println(v.multiply(BigDecimal.TEN)),
        () -> System.out.println("There is no value")
);
// 100

Optional<BigDecimal> emptyOptional = Optional.empty();
emptyOptional.ifPresentOrElse(
        (v) -> System.out.println(v.multiply(BigDecimal.TEN)),
        () -> System.out.println("There is no value!")
);
// There is no value!

Optional.get, Optional.or, Optional.orElse, Optional.orElseGet, Optional.orElseThrow

Ideally operations like get should be used once or should not used at all. In case if Optional is used with isPresent and get it is almost equivalent to “nullable value”. Because get also could throw an exception and developer should keep in mind to check if there is a value. There are better ways to work with Optional, e.g. map, filter or stream.

Anyway, if there is need to use to get check for isPresent is mandatory:

Optional<BigDecimal> optionalWithValue = Optional.of(BigDecimal.TEN);
System.out.println(optionalWithValue);
// Optional[10]
System.out.println(optionalWithValue.get());
// 10

Optional.empty().get();
// Exception in thread "main" java.util.NoSuchElementException: No value present    

Method orElse allows to define default value in case if there is no value:

Optional<Integer> optionalWithValue = Optional.of(10);
int x = optionalWithValue.orElse(20);
System.out.println(x);
// 10

Optional<Integer> emptyOptional = Optional.empty();
int y = emptyOptional.orElse(100);
System.out.println(y);
// 100

In simple example with literal 100, it works fine. But if “orElse” semantic could require computation it is better to use orElseGet which allows to specify function and evaluates its lazily only if there is no value:

Optional<Integer> emptyOptional = Optional.empty();
int y = emptyOptional.orElseGet(() -> 100);
System.out.println(y);
// 100

In case if there is need to use return Optional as default value, there is the or method:

Optional<String> abc1 = Optional.of("abc1");
Optional<String> abc2 = Optional.of("abc2");
Optional<String> emptyOptional = Optional.empty();

Optional<String> abc1OrAbc2 = abc1.or(() -> abc2);
System.out.println(abc1OrAbc2);
// Optional[abc1]

Optional<String> abc1OrEmpty = abc1.or(() -> emptyOptional);
System.out.println(abc1OrEmpty);
// Optional[abc1]

Optional<String> emptyOrAbc1 = emptyOptional.or(() -> abc1);
System.out.println(emptyOrAbc1);
// Optional[abc1]

Why to use orElseThrow which throws NoSuchElementException exception if it is identical to get method?

Optional<Integer> emptyOptional = Optional.empty();

int x = emptyOptional.orElseThrow();
// Exception in thread "main" java.util.NoSuchElementException: No value present

Semantics. Usage orElseThrow shows that a developer aware of possible exception and also shows to the reader of code that there could be an exception.

Version of orElseThrow with argument allows to specify exception:

Optional<Integer> emptyOptional = Optional.empty();

int x = emptyOptional.orElseThrow(() -> new IllegalArgumentException("There is no value!"));
// Exception in thread "main" java.lang.IllegalArgumentException: There is no value!

Optional.map, Optional.filter, Optional.flatMap, Optional.stream

A “functional way” to work with Optional type is to avoid extracting value from Optional (get method) and to avoid producing side-effects (like exceptions).

Filter

Shortly speaking, filter allows to check if value wrapped in Optional satisfies condition:

Optional<String> abc1 = Optional.of("abc1");

Optional<String> afterFilterDef2= abc1.filter((v) -> v.equals("def2"));
System.out.println(afterFilterDef2);
// Optional.empty

Optional<String> afterFilterAbc1 = abc1.filter((v) -> v.equals("abc1"));
System.out.println(afterFilterAbc1);
// Optional[abc1]

Use case example:

request.cookies.get("userId").map((c) -> Integer.valueOf(c.value)).filter(_ > 0)

Map

To preserve “unwrapping” value from Optional and map it to another value there is a map function:

Optional<String> abc1 = Optional.of("abc1");

Optional<Integer> abc1Length = abc1.map(String::length);
System.out.println(abc1Length);
// Optional[4]

FlatMap

Difference between flatMap and map:

public class App {
    static class SubscriptionPlan {
        Optional<BigDecimal> requiredPayment() {
            return Optional.of(BigDecimal.TEN);
        }
    }

    static class Customer {
        Optional<SubscriptionPlan> subscriptionPlan() {
            return Optional.of(new SubscriptionPlan());
        }
    }

    public static void main(String[] args) {
        BigDecimal amountToWithdraw =
                new Customer()
                        .subscriptionPlan()
                        .flatMap(SubscriptionPlan::requiredPayment)
                        .orElse(BigDecimal.ZERO);

        System.out.println(amountToWithdraw);
        // 10
    }
}

How this could be implemented with map?

Stream

Optional has stream?

public static void main(String[] args) {
        var l = List.of(1, 2, 3, 9, 10, 25, 36);

        var integerRoots = l.stream().flatMap(n -> integerSquareRoot(n).stream()).collect(Collectors.toList());

        System.out.println(integerRoots);
    }

    private static Optional<Integer> integerSquareRoot(int number) {
        double squareRoot = Math.sqrt(number);
        if ((int)squareRoot * (int)squareRoot == (int)number) {
            return Optional.of((int) squareRoot);
        }

        return Optional.empty();
    }

This is good example of flatMap and stream combination.

Optional.equals, Optional.hashCode and Optional.toString

Method equals is implemented in a way that a developer could think that underlying values are compared:

@Override
public boolean equals(Object obj) {
    if (this == obj) {
        return true;
    }

    if (!(obj instanceof Optional)) {
        return false;
    }

    Optional<?> other = (Optional<?>) obj;
    return Objects.equals(value, other.value);
}

To avoid misunderstanding:

Optional<String> abc1 = Optional.of("abc");
Optional<String> abc2 = Optional.of("abc");
Optional<String> xyz = Optional.of("xyz");
Optional<String> empty1 = Optional.empty();
Optional<String> empty2 = Optional.empty();
Optional<String> emptyString = Optional.of("");

System.out.println(abc1.equals(abc2));
// true
System.out.println(abc1.equals(xyz));
// false
System.out.println(abc1.equals(empty1));
// false
System.out.println(empty2.equals(emptyString));
// false
System.out.println(empty1.equals(empty2));
// true

Method hashCode also uses hashCode function on underlying value:

@Override
public int hashCode() {
    return Objects.hashCode(value);
}

E.g. for String:

System.out.println(Optional.of("abc").hashCode());
// 96354
System.out.println("abc".hashCode());
// 96354

So, Optional values could be compared or used as dictionary keys if underlying values do not restrict this.

Method toString prints human-readable contents of Optional object:

System.out.println(Optional.of("some value"));
// Optional[some value]
System.out.println(Optional.of(""));
// Optional[]
System.out.println(Optional.empty());
// Optional.empty

In other languages

Option in Scala

In Scala there is Option monad which sounds quite similar to Optional in Java, but really it is not. In Java Optional has been introduced to solve simple issue - denote that function may return anything, but in Scala Option type is first-class citizen which plays very important role as part of language.

Option in Scala has a very powerful API and it is Serializable.

In Scala it is integrated into standard library, e.g. Map.get(key):

trait Map[K, +V] ... {
    // ... 
    abstract def get(key: K): Option[V]
    // ... 
}

In Scala Option supports pattern matching:

val nameMaybe = request getParameter "name"
nameMaybe match {
  case Some(name) =>
    println(name.trim.toUppercase)
  case None =>
    println("No name value")
}

It is also supports collection-like functions:

val name = Some("John")

name.foreach(n => println(n))
// John

An integral part of the language.

Maybe in Haskell

In Haskell there is no concept of nil or null value to say that function does not return a value. But there is a very straightforward way to say it - monad Maybe:

data Maybe a = Just a | Nothing

This Maybe holds just a value or represents Nothing.

Optional in Java is not a monad

Optional type in Java “behaves like” a monad. It has constructor unit - ofNullable and bind - flatMap methods. And it seems that it could be as the monad, but it could not. Because Optional breaks the monad laws.

Developer should use Optional type in Java very carefully.

Summary

Optional type allows to increase readability and prevent errors if used carefully, so it is recommended to:

  1. Avoid using optionality at all.
  2. But if it is not possible, prefer Optional instead of null.
  3. In performance critical blocks, local or private code blocks, Optional could be omitted.

Links

  1. http://www.baeldung.com/java-optional
  2. https://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html
  3. https://dzone.com/articles/java-8-optional-whats-point
  4. https://homes.cs.washington.edu/~mernst/advice/nothing-is-better-than-optional.html
  5. https://checkerframework.org/manual/#nullness-checker
  6. https://blog.codefx.org/java/dev/why-isnt-optional-serializable/
  7. https://danielwestheide.com/blog/2012/12/19/the-neophytes-guide-to-scala-part-5-the-option-type.html
  8. https://pbrisbin.com/posts/maybe_is_just_awesome/
  9. http://cr.openjdk.java.net/~jrose/values/values-0.html
  10. https://danielwestheide.com/blog/2016/04/26/when-option-is-not-good-enough.html
  11. https://www.sitepoint.com/how-optional-breaks-the-monad-laws-and-why-it-matters/
  12. https://medium.com/@afcastano/monads-for-java-developers-part-1-the-optional-monad-aa6e797b8a6e
  13. https://medium.com/coding-with-clarity/understanding-the-optional-monad-in-java-8-e3000d85ffd2
  14. https://www.scala-lang.org/api/current/scala/Option.html