Java Optional

Optional class was introduced in Java 8 to better handle null values.

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

Creation

Optional.ofNullable(T)

Here Optional.ofNullable() can accept null and non null value.

1
Optional<String> testString = Optional.ofNullable(s);

Optional.empty()

No value is present in this case

1
2
@Test
Optional<String> empty = Optional.empty();

Optional.of(T)

Returns Optional with specified present non-null value. Use this method only if you are sure the parameter is NOT null.

1
Optional<String> notEmpty = Optional.of("foo");

Operation

Optional.isPresent()

1
boolean isPresent()

Optional.isPresent() returns true if the given Optional object is non-empty. Otherwise it returns false.

1
2
3
4
Optional<String> opt = Optional.of("foo");
if(opt.isPresent()){
System.out.println(opt.get());
}

Optional.isEmpty

1
boolean isEmpty()

Java 11 provides isEmpty() method which is the opposite of isPresent(). If a value is not present, returns true, otherwise false.

1
2
3
4
Optional<String> optString = Optional.empty();
if(optString.isEmpty()){
System.out.println("optString is empty!");
}

Optional.ifPresent(Consumer)

1
void ifPresent(Consumer<? super T> action)

Optional.ifPresent() performs given action if the given Optional object is non-empty. Otherwise it returns false.

1
2
3
4
5
@Test
public void testIfPresent() {
Optional<String> opt = Optional.ofNullable("FOO");
opt.ifPresent(name -> System.out.println(name));
}

Optional.orElse(T other)

1
T orElse(T other)

It returns the value if present in Optional Container. Otherwise returns other.

1
2
3
4
5
6
@Test
public void testOrElse() {
Optional<String> nullName = Optional.empty();
String name = nullName.orElse("default");
assertEquals("default", name);
}

Optional.orElseGet(Supplier)

1
T orElseGet(Supplier<? extends T> supplier)

Return the value if present, otherwise invoke other and return the result of that invocation.

1
2
3
4
5
6
@Test
public void testOrElseGet() {
String nullName = null;
String name = Optional.ofNullable(nullName).orElseGet(() -> "john");
assertEquals("john", name);
}

Optional.orElse vs Optional.orElseGet

orElse will always create the default value. orElseGet will invoke the supplier to calculate the value to return.

Optional.or()

1
Optional<T> or(Supplier<? extends Optional<? extends T>> supplier)

If a value is present, returns an Optional describing the value, otherwise returns an Optional produced by the supplying function.

1
2
3
4
5
6
@Test
public void testOr() {
String nullName = null;
Optional<String> name = Optional.ofNullable(nullName).or(() -> Optional.ofNullable("john"));
assertEquals("john", name.get());
}

Optional.orElseThrow()

1
2
T orElseThrow()
<X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X

Return the contained value, if present, otherwise throw an exception. If no argument is provieded, NoSuchElementException will be thrown

1
2
3
4
5
@Test(expected=IllegalArgumentException.class)
public void testOrElseThrow() {
String nullName = null;
String name = Optional.ofNullable(nullName).orElseThrow(IllegalArgumentException::new);
}

Optional.get()

1
T get()

This method can throw NoSuchElementException exception.

DO NOT call Optional.get method unless you can prove it will never be null; instead use one of the safe methods like ifPresent or orElse.

You can always replace get() with other methods like orElse(), orElseThrow(), ifPresent(), map() or flatMap().

1
2
3
4
Optional<String> opt = Optional.of("foo");
if(opt.isPresent()){
System.out.println(opt.get());
}

Optional.filter(Predicate)

1
Optional<T> filter(Predicate<? super T> predicate)

If a value is present, and the value matches the given predicate, return an Optional describing the value, otherwise return an empty Optional.

1
2
Optional<String> opt = Optional.ofNullable("");
opt.filter(s -> !s.isEmpty()).ifPresent(s -> System.out.println("Non empty String value is " + s));

Optional.map()

1
<U> Optional<U> map(Function<? super T,? extends U> mapper)

If a value is present, apply the provided mapping function to it, and if the result is non-null, return an Optional describing the result.
Otherwise return an empty Optional.

1
2
3
4
5
6
7
8
9
@Test
public void testMap() {
String name = "foo";

Optional<String> nameOptional = Optional.ofNullable(name);

int len = nameOptional.map(String::length).orElse(0);
assertEquals(3, len);
}

Optional.flatMap()

1
<U> Optional<U> flatMap(Function<? super T,? extends Optional<? extends U>> mapper)

flatMap does the mapping, then flats the results. This avoids returning values of type Optional<Optional<String>>. Use Optional.flatMap() when the mapping function returns an Optional.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public String myMapFunction(String s) {
return s.toUpperCase();
}

public Optional<String> myMapFunction2(String s) {
return Optional.ofNullable(s).map(String::toUpperCase);
}

@Test
public void test() {
Optional<String> opt = Optional.ofNullable("foo");

// map
assertEquals(Optional.of("FOO"), opt.map(s -> myMapFunction(s)));

// map
assertEquals(Optional.of(Optional.of("FOO")), opt.map(s -> myMapFunction2(s)));

// use flatmap with function that returns Optional
assertEquals(Optional.of("FOO"), opt.flatMap(s -> myMapFunction2(s)));
}

Optional.stream()

1
Stream<T> stream()

This method is introduced in Java 9.

If a value is present, returns a sequential Stream containing only that value, otherwise returns an empty Stream.

Example to get all comments from an optional post that is less than 100 characters. This is very hard to code with Optional methods alone. So have to convert to stream using stream method.

1
2
3
4
5
6
Optional<Post> optionalPost = Optional.ofNullable(post1);
List<Comment> shortComments = optionalPost.stream()
.flatMap(post -> post.getComments().stream())
.filter(comment -> comment.getContent().length() < 100)
.toList();
shortComments.forEach(System.out::println);

Best Practice

DO NOT use Optional as a field because Optional is not Serializable.

DO NOT use Optional as parameter for methods or constructor because this will make method call complicated.

You can use Optional as the getter return type, but do not over-use it. see Should Java 8 getters return optional type?

Optional is intended to be used as a method return type. This helps create fluent readable code.

References