kamer.dev

Java Optional Guide

- #java

1. What is the purpose of Optional Class?

If you have been into coding for a while, you should have encountered Null Pointer Exception before. If you try to use a reference type that doesn’t point anything in the memory, you get NullPointerException. This sentence may be complicated, see the example below:

String name = null;
System.out.println(name);

I declared a name variable that points to null and tried to print it. This will throw NullPointerException since name variable doesn’t show anywhere in the memory. Generally, you do not assign null to a variable and print it. But there are many cases you may accidentally see this runtime exception. If you want to see all cases read docs.

Programmers are used to deal with this exceptions by null checking. (if(name != null)). But this makes the code ugly and a programmer cannot be sure if there is a possibility of null return from a method he/she called. Many of the programming languages have different methods to deal with Null Pointer Exception. Java Optional is one of them since Java 8. Optionals are objects that may have null or non-value inside. You can check an optional object whether the value is null or not and get the value if it is not. Returning an Optional from a method is simply saying “check the returning object since it can have null value."

2. Creating Optional Objects

2.1 Empty Optional (empty())

Returning an empty optional is equivalent to returning null before Java 8. This can be done with the static empty() method.

Optional<String> optionalName = Optional.empty();

Never check an optional if it is empty by comparing it == Optional.empty(); There are different methods to check an Optional.

2.2 Optional with a value (of())

Returning an optional with a value inside it is equivalent to returning the object itself before Java 8.

Optional<String> optionalString = Optional.of("Kamer");

of() method is not nullable. If you pass null, you will get NullPointerException.

2.3 Optional with a nullable value (ofNullable())

This method is for nullable values. You can pass any value whether it is null or

Optional<String> optionalString = Optional.ofNullable(null);

Optional<String> optionalString = Optional.ofNullable("Kamer");

3. Checking Optionals

3.1 Checking with isPresent();

It does what the name suggests. Returns boolean value.


if (optionalName.isPresent()){
    ...
    ...
}

3.2. Checking with isEmpty();

Opposite of isPresent(). This method can be used since Java 11.


if (optionalName.isEmpty()){
    ...
    ...
}

4. Getting Value From Optional

As you see, Optional is a container that holds some value. After checking an Optional with the methods above, you can get the value with get() method.

if (optionalUser.isPresent()){
    return optionalUser.get();
} else {
    throw new UserNotFoundException();
}

5. Default Values and Actions

Sometimes we may return a default value, produce a value or do some actions in case of any empty Optional.

5.1 Get a default value with orElse()

It returns the value if optional has a value and returns the default value if not so.

String userName = optionalUsername.orElse("admin");

5.2 Throw NoSuchElementException with orElseThrow()

Title is clear. This method is available since Java 10.

...
return optionalUsername.orElseThrow();

5.3 Throw any exception with orElseThrow()

You can throw any exception you want if Optional is empty.

...
return optionalUsername.orElseThrow(UserNotFoundException::new);
...
return optionalUsername.orElseThrow(() -> new UserNotFoundException("..."));

5.4 Get a default value with a Supplier with orElseGet()

Supplier is the function that takes no value and produces a result. See java.util.function.Supplier.

...
return optionalUsername.orElseGet(String::new);

5.5 Return a default Optional with or()

This function also takes Supplier function as parameter. or() returns the Optional or the default Optional that supplier function produces. This method is available since Java 9.

...
return optionalUsername.or(() -> Optional.of("admin"));

6. Conditional Actions with Lambda Expressions

6.1 ifPresent()

This method takes Consumer<? super T> as parameter. See java.util.function.Consumer.

optionalUsername.ifPresent(System.out::println);

6.2 ifPresentOrElse()

This method takes two parameters. Consumer<? super T> and Runnable. It is available since Java 9.

optionalUsername.ifPresentOrElse(
    System.out::println,
    () -> System.out.println("No Username."));

7. Filtering the Value with filter()

This method takes a predicate and an Optional is returned based on the result of the predicate. If predicate returns true Optional has the value. Otherwise, empty Optional is returned.

...
return optionalUsername.filter(String::isBlank);

8. Mapping Values

8.1 map()

It returns an Optional. If Optional is not empty, it has the state after mapping function is applied.

Optional<String> optionalUsername = optionalResult.map(String::trim);

8.2 flatMap()

It’s like map() but this time the result is flattened. This means if there is more than one level nested Optional, it converts it to one-level. See the example.

Optional<String> optionalUsername = Optional.ofNullable("Kamer");

Optional<Object> mapOptional
    = optionalUsername.map(username -> Optional.of(username.toLowerCase()));

This map() return will return the String inside an Optional which is inside another Optional. Optional<Optional<String>>

Optional<String> optionalUsername = Optional.ofNullable("Kamer");

Optional<Object> flatMapOptional
    = optionalUsername.flatMap(s -> Optional.of(s.toLowerCase()));

But flatMap() flattens the result and it returns just Optional<String>

References