Optional in Java: Everything You Need To Know

Optional in Java

Java’s Optional class was introduced in 2014 to allow developers to represent the empty state. But what exactly does that mean? And why is it important enough to bother using Optional?

In this article you’ll learn everything you need to know about Optional: how it works, what problems it helps solve, and when not to use it.

1. What actually is Optional?

Optional is simply a Java class located in the java.util package and available within the Java Development Kit (JDK). That means we can use it in anywhere in our Java code, simply by creating an instance of that class.

But before we do that, it’s helpful to understand that Optional is simply a container for an object. And just like real-life containers, it can have one of 2 states:

  1. contains an item (also known as present)
  2. doesn’t contain an item (also known as empty)

Take my glasses case, for example. It either contains my glasses or doesn’t contain my glasses.

My glasses case is a kind of container, just like Optional.

Amazing, I know!

In Java we create an Optional with a specific state. At the point of creation we either give it an object, or don’t give it an object.

Here’s how to create an Optional which contains an object.

Glasses glasses = new Glasses();
Optional<Glasses> glassesOptional = Optional.of(glasses);

Creating an Optional which doesn’t contain an object is even simpler.

Optional<Glasses> glassesOptional = Optional.empty();

Optional type: notice how when we assign an Optional to a variable, we specifcy a type (also known as a class) within <>? This uses generics to tell the Optional object what type of thing it contains. This is useful when you want to retrieve the value, as you’ll see soon.

OK, so at this point you might be thinking What’s the point?

To truly answer that we have to travel back in time to see how things were done before Optional even existed.

2. What problem does Optional solve?

Imagine it’s 2013 and you’re happily coding away on Java 7. Ah, bless!

You’re coding a REST API which exposes the world’s greatest guitarists. 🎸 Right now, you need to create a Java method which searches by the guitarist’s last name.

You add this method to the GuitaristService class. Right now it only supports a single guitarist, the infamous Jimi Hendrix. It looks up the guitarist and returns it, if found.

public Guitarist findByLastName(String lastName) {
    if (lastName.equalsIgnoreCase("Hendrix")) {
        return new Guitarist("Jimi", "Hendrix", "Purple Haze");
    }
    return null;
}

But what if the guitarist doesn’t exist? In this scenario, you think the best thing to do is to return null. Whoever is writing the code that calls this method will surely understand that null means no value, right?

Calling a method which can return null

Let’s look at it from the perspective of the code that calls findByLastName. That’s the getGuitarist method, which directly handles the API call.

@GetMapping(value = "/guitarist/{lastName}", produces = MediaType.APPLICATION_JSON_VALUE)
public Guitarist getGuitarist(@PathVariable String lastName) {
    return guitaristService.findByLastName(lastName);
}

Whoever implemented the method didn’t think about the scenario where no guitarist is found. In that case, the method returns null and whoever called the REST API gets an empty response body.

$ #let's request a known guitarist
$ curl http://localhost:8080/guitarist/hendrix
{"id":1,"firstName":"Jimi","lastName":"Hendrix","signatureSong":"Purple Haze"}$
$
$ #let's request an unknown guitarist
$ curl http://localhost:8080/guitarist/green
$
$ #no response body ^

We could of course add a null check to return a 404 Not Found response.

@GetMapping(value = "/guitarist/{lastName}", produces = MediaType.APPLICATION_JSON_VALUE)
public Guitarist getGuitarist(@PathVariable String lastName) {
    Guitarist guitarist = guitaristService.findByLastName(lastName);
    if (guitarist == null) {
        throw new ResponseStatusException(NOT_FOUND, "Unable to find guitarist.");
    }
    return guitarist;
}

But there’s nothing that forces our hand to handle this scenario. As developers, our poor brains have to remember to do this every time.

A chance of NullPointerException

Even worse would be if we had to access some fields of the guitarist object.

@GetMapping(value = "/guitarist/{lastName}", produces = MediaType.APPLICATION_JSON_VALUE)
public Guitarist getGuitarist(@PathVariable String lastName) {
    Guitarist guitarist = guitaristService.findByLastName(lastName);
    if (guitarist.getSignatureSong().equals("Stairway to heaven")) {
        // increment cheese factor
    }
    return guitarist;
}

Since the guitarist is null, Java throws a NullPointerException and whoever is calling our REST API gets a nasty 500 error.

$ curl http://localhost:8080/guitarist/green
{"timestamp":"2022-07-17T08:56:34.363+00:00","status":500,"error":"Internal Server Error","trace":"java.lang.NullPointerException...

We can of course write code to handle the null scenario.

@GetMapping(value = "/guitarist/{lastName}", produces = MediaType.APPLICATION_JSON_VALUE)
public Guitarist getGuitarist(@PathVariable String lastName) {
    Guitarist guitarist = guitaristService.findByLastName(lastName);
    if (guitarist != null && guitarist.getSignatureSong().equals("Stairway to heaven")) {
        // increment cheese factor
    }
    return guitarist;
}

Once again, this relies on whoever is calling the method being disciplined enough to remember to handle null.

null can spread like a virus

If methods in a codebase can return null, developers may start to code defensively. They perform null checks on values that could never actually be null, creating verbose and difficult to understand code.

The problem is that in Java 7 there’s no easy way to tell the caller of the method that the guitarist doesn’t exist. Returningnull is not explicit. It could mean that the guitarist doesn’t exist, but it could also signify some other unexpected problem.

Fortunately, Java 8 offers a solution to resolve all these issues. All neatly packaged in a new class called, you guessed it, Optional.

What about exceptions? Throwing a checked exception instead of returning null is an alternative solution that’s more explicit, since it forces the caller to handle the empty scenario. Unfortunately though, it’s quite verbose because you need to a) create a new exception type and b) handle the exception with a try catch statement.

3. How to use Optional to return an empty value?

Now let’s look at how to use Optional to better handle returning an empty value from a method.

We’ll go back to our method which looks up guitarists by last name. Here’s how we use Optional to either return the Jimi Hendrix guitarist or return empty.

public Optional<Guitarist> findByLastName(String lastName) {
    if (lastName.equalsIgnoreCase("Hendrix")) {
        return Optional.of(new Guitarist("Jimi", "Hendrix", "Purple Haze"));
    }
    return Optional.empty();
}

There are 3 main takeaways from this:

  1. the method signature returns Optional<Guitarist>, or in other words an Optional of type Guitarist
  2. if lastName is Hendrix, we construct a Guitarist object and pass it to Optional.of. This creates an Optional containing the Guitarist, which we return from the method.
  3. if lastName is anything else, we return Optional.empty(). This creates an empty Optional, which will force whoever is calling the method to handle the guitarist not found scenario, as you’ll see shortly.

So our method now returns an Optional, which will either contain Jimi Hendrix or contain nothing. Groovy!

But how do we actually use the Optional and access the value contained within?

4. How to handle an Optional returned by a method?

The great thing about calling a method that returns an Optional is that we immediately know that the object can be in one of 2 states:

  1. contains a value
  2. is empty

Java’s Optional offers a rich API to help with:

  • accessing the value contained within the Optional, if present, and processing it in some way
  • doing something else if the Optional is empty

All this is done in a very safe way, avoiding any chance of NullPointerException. And it’s a lot less verbose than if we were using null values.

In the next example, we want to call the findByLastName method from earlier, passing Hendrix to try to retrieve the Hendrix guitarist.

Optional<Guitarist> guitarist = guitaristService.findByLastName("Hendrix");

From this perspective, we don’t know whether the returned Optional will contain Hendrix or nothing at all. But we do know there’s a chance of either happening.

Handling the present value within an Optional

Here’s how we can handle the scenario where the guitarist is present, using the ifPresent method.

// returns Optional with value
Optional<Guitarist> lookupResult = guitaristService.findByLastName("Hendrix");
lookupResult.ifPresent(guitarist -> System.out.println(guitarist.getSignatureSong()));

When we execute this code it prints out Purple Haze.

The ifPresent method takes a consumer function, passed as a lambda expression. The function gets executed if the Optional contains a value. That’s perfect for when we want to handle the value in some way. And we don’t even have to use an if statement.

Note how the consumer function accepts a guitarist, and then prints out the result of getSignatureSong. It’s thanks to the generics used by Optional that we can access methods from guitarist in this way.

Jimi Hendrix: a big fan of Java’s Optional

Handling an empty value within an Optional

So what about handling the case when the Optional is empty?

Well there are various ways to do this, which we’ll explore in detail later. For now though, here’s how to execute another function in the empty case using ifPresentOrElse. Like ifPresent it takes a function which is executed when the Optional has a value, but it also takes a 2nd function which is executed when the Optional is empty.

// returns empty Optional
Optional<Guitarist> lookupResult = guitaristService.findByLastName("Page");
lookupResult.ifPresentOrElse(
        guitarist -> System.out.println(guitarist.getSignatureSong()),
        () -> System.out.println("Guitarist not found!")
);

When we execute this code it prints out Guitarist not found!

We’ve deliberately requested a guitarist that doesn’t exist, so ifPresentOrElse will automatically call the 2nd function. Pretty cool!

Substituting a default value

What if we want to substitute a default value in the case where findByLastName returns an empty Optional? That’s simple using orElse, which either returns the value inside the Optional, if present, or subsitutes the provided value if empty.

Guitarist defaultGuitarist = new Guitarist("Ed", "Sheeran", "The A team");
// returns empty Optional
Guitarist guitarist = guitaristService.findByLastName("Page").orElse(defaultGuitarist);
System.out.println("Recommended listening: " + guitarist.getSignatureSong());

When we execute this code it prints out Recommended listening: The A team.

We’re calling orElse directly on the Optional returned by findByLastName. Since the Optional is empty, this method is clever enough in this case to return defaultGuitarist.

Throwing an exception

Now that you’ve seen a few use cases using Optional, let’s go back to the original bad code which handles the REST API request.

We can update it to handle the empty value by throwing an exception using the orElseThrow method. We just need to pass a suppiler function to return the exception.

@GetMapping(value = "/guitarist/{lastName}", produces = MediaType.APPLICATION_JSON_VALUE)
public Guitarist getGuitarist(@PathVariable String lastName) {
    return guitaristService.findByLastName(lastName)
            .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Guitarist not found."));
}

This means when we request an unknown guitarist we get the appropriate 404 response. But the code is now less prone to the introduction of NullPointerException and also less verbose.


At this point hopefully you’ve got a grasp of what Optional is all about. There are in total 17 methods you can call on the Optional class, and so far we’ve covered 6 of them: of, empty, ifPresent, ifPresentOrElse, orElse, and orElseThrow.

Now we’re going to pick up the pace to cover the others, including when you would use them. But first let’s explore some scenarios when you shouldn’t use Optional.

5. When should you not use Optional?

You’ve already seen how Optional can be a useful tool when a method needs to return an empty value, and you don’t want to return null. This is the main intended use case for Optional.

In our enthusiasm, it might be easy to get carried away and start using Optional in other ways. Some of these have bad consequences, so read on to learn when not to use Optional.

Avoid using Optional as a method parameter

If Optional either contains or doesn’t contain a value, shouldn’t we be able to use it as a method parameter as well as a return type?

public Optional<Guitarist> findByName(String firstName, Optional<String> lastName) {
    // do something
}

Whilst this is tempting, if you think through the consequences it becomes clear that it’s a bad idea. When you do this you’ve now got 3 states that you need to handle within your method.

  1. an Optional with a value
  2. an empty Optional
  3. null

Contrast this with this signature, where you only have 2 states to handle: a value or null.

public Optional<Guitarist> findByName(String firstName, String lastName) {
    // do something
}

A much better approach to writing a method which has an optional parameter is to overload the method. Or in other words, write another method that doesn’t take the parameter.

Here’s how it looks for the findByName method.

public Optional<Guitarist> findByName(String firstName)
    // do something
}

public Optional<Guitarist> findByName(String firstName, String lastName) {
    // do something
}

This makes it clearer for someone calling the method to know that they can leave out the lastName parameter.

Avoid using Optional as a class variable

Here’s a class to represent a famous guitarist.

public class Guitarist {
    private String lastName;
    private Integer ageAtDeath;

    // constructor & getter methods
}

Although a guitarist will always have a lastName, ageAtDeath might be null for famous guitarists who are still alive. Yes, there are still a few!

Wouldn’t this be a good use case for Optional? How about this code, then?

public class Guitarist {
    private String lastName;
    private Optional<Integer> ageAtDeath;

    // constructor & getter methods
}

If we were to implement the above solution, we’d have these potential bad consequences:

1) serializing Optional causes NotSerializableException: if we use Optional as a variable in a class that implements Serializable, when we try to write an object to an output stream then Java throws an exception.

@AllArgsConstructor
@Getter
private static final class Guitarist implements Serializable {
    private String lastName;
    private Optional<Integer> ageAtDeath;
}

@Test
public void throwsNotSerializableException() throws IOException {
    Guitarist guitarist = new Guitarist("Hendrix", Optional.of(27));

    NotSerializableException exception = assertThrows(
            NotSerializableException.class,
            () -> new ObjectOutputStream(new ByteArrayOutputStream()).writeObject(guitarist)
    );

    assertEquals("java.util.Optional", exception.getMessage());
}

Annotations: some examples in this article use annotations like @AllArgsConstructor and @Getter. These are from the Lombok library, and reduce boilerplate code to make the examples easier to understand.

2) writing Optional to JSON produces undesired results: when writing an object which contains an Optional to JSON, the tool used will likely include the result of the isPresent and isEmtpy methods in the resulting JSON.

@AllArgsConstructor
@Getter
private static final class Guitarist implements Serializable {
    private String lastName;
    private Optional<Integer> ageAtDeath;
}

@Test
public void serializesOptionalToJSONBadly() throws IOException {
    ObjectMapper objectMapper = new ObjectMapper();

    String json = objectMapper.writeValueAsString(new Guitarist("Knopfler", Optional.empty()));

    assertEquals("{\"lastName\":\"Knopfler\",\"ageAtDeath\":{\"empty\":true,\"present\":false}}", json);
}

Optional with Jackson: if you’re using the Jackson JSON library, you can generate the desired JSON by registering the Jdk8Module on your ObjectMapper. Check out this code example.

As an alternative to using Optional for a class variable, consider just setting the value to null. When you retrieve the value, you can pass it to Optional.ofNullable, which will either create an Optional with a value or an empty Optional. You can then process the Optional as you like.

Here’s an example which shows how the ageAtDeath class variable can be processed in a null safe way using ofNullable.

@Getter
@Builder
@AllArgsConstructor
private static final class Guitarist {
    private String lastName;
    private Integer ageAtDeath;
}

@Test
public void ofNullableWithValue() {
    Guitarist guitarist = Guitarist.builder().lastName("Hendrix").ageAtDeath(27).build();

    Optional.ofNullable(guitarist.getAgeAtDeath()).ifPresentOrElse(
            age -> System.out.println("Died at age " + age), 
            () -> System.out.println("Still alive!")
    );

    assertEquals("Died at age 27", outputStreamCaptor.toString().trim());
}

@Test
public void ofNullableWithNull() {
    Guitarist guitarist = Guitarist.builder().lastName("Knopfler").build();

    Optional.ofNullable(guitarist.getAgeAtDeath()).ifPresentOrElse(
            age -> System.out.println("Died at age " + age), 
            () -> System.out.println("Still alive!")
    );

    assertEquals("Still alive!", outputStreamCaptor.toString().trim());
}

Avoid using the Optional methods get(), isPresent(), and isEmpty()

As you saw earlier, the Optional method ifPresent provides a way to handle its contained value via a fluid API.

@Test
public void canUseIfPresent() {
    Guitarist knopfler = new Guitarist("Mark", "Knopfler", "Money For Nothing");
    Optional<Guitarist> guitaristOptional = Optional.of(knopfler);

    guitaristOptional.ifPresent(guitarist -> System.out.println(guitarist.getSignatureSong()));

    assertEquals("Money For Nothing", outputStreamCaptor.toString().trim());
}

Another way to achieve the same outcome would be to combine 2 other Optional methods:

  1. isPresent() returns true if the Optional contains a value
  2. get() returns the value contained within the Optional, or throws NoSuchElementException if the Optional is empty

Here’s an example of using this approach, which prints the same value.

@Test
public void canUseIsPresentAntiPattern() {
    Guitarist knopfler = new Guitarist("Mark", "Knopfler", "Money For Nothing");
    Optional<Guitarist> guitaristOptional = Optional.of(knopfler);

    if (guitaristOptional.isPresent()) {
        System.out.println(guitaristOptional.get().getSignatureSong());
    }

    assertEquals("Money For Nothing", outputStreamCaptor.toString().trim());
}

As you can see, we need to excplicitly check that isPresent returns true before calling get, to avoid a potential NoSuchElementException.

This additional complexity, and also verbosity, is a reason to avoid using these methods. Instead, chain together methods like ifPresent to achieve the same outcome.

For the same reasons, using isEmpty should also be avoided.


Now that you’ve got a better understanding of when not to use Optional, let’s jump into some more advanced examples of when using it is a great idea, while exploring its other methods.

6. What are some other common use cases for Optional?

Here are some quickfire examples of different patterns you can use with Optional. Each example takes the form of a test case, so you can easily see the expectation.

Check out the code on GitHub to try them out for yourself.

Return a default value with orElse

On an Optional you can call orElse which will either return the value, if present, or otherwise the provided default value.

@Test
public void orElsePresent() {
    Optional<String> emptyOptional = Optional.of("Jimmy");
    assertEquals("Jimmy", emptyOptional.orElse("Trevor"));
}

@Test
public void orElseEmpty() {
    Optional<String> emptyOptional = Optional.empty();
    assertEquals("Trevor", emptyOptional.orElse("Trevor"));
}

This is ideal for substituting a default value, without the need for an if statement.

Return a calculated default value with orElseGet

The orElseGet method is similar to orElse, except this time you can pass a function to calculate a value returned when the Optional is empty.

@Test
public void orElseGetWhenPresent() {
    Optional<String> emptyOptional = Optional.of("Jake");
    assertEquals("Jake", emptyOptional.orElseGet(this::generateSubstituteName));
}

@Test
public void orElseGetWhenEmpty() {
    Optional<String> emptyOptional = Optional.empty();
    assertEquals("Karen", emptyOptional.orElseGet(this::generateSubstituteName));
}

private String generateSubstituteName() {
    return "Karen";
}

If your supplier function is slow or consumes unnecessary resources, using orElseGet ensures it’s only called when necessary.

Return a calculated default Optional with or

Calling or on an Optional will return the entire Optional, if present, otherwise the supplied Optional.

@Test
public void orWhenPresent() {
    Optional<String> presentOptional = Optional.of("Jimmy");
    Optional<String> substituteOptional = Optional.of("Jake");

    assertEquals(presentOptional, presentOptional.or(() -> substituteOptional));
}

@Test
public void orWhenEmpty() {
    Optional<String> emptyOptional = Optional.empty();
    Optional<String> substituteOptional = Optional.of("Jake");

    assertEquals(substituteOptional, emptyOptional.or(() -> substituteOptional));
}

Why is this useful? Consider this example, which shows how multiple Optional objects can be combined with or.

Optional<Guitarist> database1Optional = database1.lookupGuitarist("Hendrix");
Optional<Guitarist> database2Optional = database2.lookupGuitarist("Hendrix");

database1Optional.or(database2Optional)
        .ifPresent(guitarist -> System.out.println("Found Jimi Hendrix!"));

Throw an exception when empty with orElseThrow

When an empty Optional is a scenario you can’t handle, call orElseThrow to throw a NoSuchElementException exception.

@Test
public void orElseThrowWhenPresent() {
    Optional<String> presentOptional = Optional.of("Jimmy");
    assertDoesNotThrow(() -> presentOptional.orElseThrow());
}

@Test
public void orElseThrowWhenEmpty() {
    Optional<String> emptyOptional = Optional.empty();
    assertThrows(NoSuchElementException.class, emptyOptional::orElseThrow);
}

But in some scenarios it might be more appropriate to throw another type of exception. In this case, call orElseThrow passing a supplier function which returns an exception object.

@Test
public void orElseThrowWithSupplierWhenPresent() {
    Optional<String> presentOptional = Optional.of("Jimmy");
    assertDoesNotThrow(
            () -> presentOptional.orElseThrow(() -> new RuntimeException("Oops"))
    );
}

@Test
public void orElseThrowWithSupplierWhenEmpty() {
    Optional<String> emptyOptional = Optional.empty();
    assertThrows(RuntimeException.class, 
            () -> emptyOptional.orElseThrow(() -> new RuntimeException("Oops"))
    );
}

7. What are some advanced Optional patterns?

At this point we’ve covered 13 of the available Optional methods, so here are the final 4 slightly more advanced use cases.

Apply a condition with filter

An Optional which contains a value can be tested to ensure that it meets a given condition. If not, then it is transformed into an empty Optional.

@Test
public void filterTrueWhenPresent() {
    Optional<Guitarist> jimiHendrix = Optional.of(new Guitarist("Jimi Hendrix", 27));

    Optional<Guitarist> filteredOptional = jimiHendrix.filter(guitarist -> guitarist.getAgeAtDeath() < 30);

    assertEquals(jimiHendrix, filteredOptional);
}

@Test
public void filterFalseWhenPresent() {
    Optional<Guitarist> peterGreen = Optional.of(new Guitarist("Peter Green", 73));

    Optional<Guitarist> filteredOptional = peterGreen.filter(guitarist -> guitarist.getAgeAtDeath() < 30);

    assertEquals(Optional.empty(), filteredOptional);
}

@Test
public void filterWhenEmpty() {
    Optional<Guitarist> emptyGuitarist = Optional.empty();

    Optional<Guitarist> filteredOptional = emptyGuitarist.filter(guitarist -> guitarist.getAgeAtDeath() < 30);

    assertEquals(Optional.empty(), filteredOptional);
}

Of course, if the original Optional is empty, then an empty Optional is returned.

Convert to another Optional type with map

If you’d like to convert a value contained within an Optional to another type of object, then call map passing a function.

@Test
public void mapWhenPresent() {
    Optional<Guitarist> jimiHendrix = Optional.of(new Guitarist("Jimi Hendrix", 27));
    assertEquals(Optional.of("Jimi Hendrix"), jimiHendrix.map(Guitarist::getName));
}

@Test
public void mapWhenEmpty() {
    Optional<Guitarist> emptyGuitarist = Optional.empty();
    assertEquals(Optional.empty(), emptyGuitarist.map(Guitarist::getName));
}

In this case, the function passed to map converts from Guitarist to a String. The map method automatically wraps the returned value inside of an Optional.

If the original Optional is empty, then map returns an empty Optional.

Convert to another Optional type with flatMap

The flatMap method is almost the same as map, except this time the function you pass to it must return an Optional.

    @Test
    public void flatMapWhenPresent() {
        Optional<Guitarist> jimiHendrix = Optional.of(new Guitarist("Jimi Hendrix", 27));
        assertEquals(Optional.of("Jimi Hendrix"), 
                jimiHendrix.flatMap(guitarist -> Optional.of(guitarist.getName())));
    }

    @Test
    public void flatMapWhenEmpty() {
        Optional<Guitarist> emptyGuitarist = Optional.empty();
        assertEquals(Optional.empty(), 
                emptyGuitarist.flatMap(guitarist -> Optional.of(guitarist.getName())));
    }

Note how flatMap doesn’t wrap the return value of the function in an Optional.

Convert an Optional to a Stream with stream. Yes, really!

The Java Stream API lets you process a collection of items in some way, through a pipeline of steps. It’s a rich API, so converting an Optional to a Stream will likely be useful in some scenarios.

The stream method converts an Optional to a Stream of length 1 or 0, depending on whether it has a value or not.

Let’s say we want to lookup 3 guitarists by name, one-by-one, within a guitaristRepository which returns Optional<Guitarist>. In this example, the repository returns an Optional with a value for Hendrix and Cobain, but an empty Optional for Sheeran (sorry Ed!).

We’d like to end up with a List of only those guitarists who were found in the repository.

@Test
public void stream() {
    GuitaristRepository guitaristRepository = new GuitaristRepository();
    List<String> guitaristsToFindByName = asList("Hendrix", "Sheeran", "Cobain");

    List<Guitarist> foundGuitarists = guitaristsToFindByName.stream()
            .map(guitaristRepository::findGuitaristByName)
            .flatMap(Optional::stream)
            .toList();

    assertEquals(asList(new Guitarist("Hendrix", 27), new Guitarist("Cobain", 27)), foundGuitarists);
}

We convert the List to a Stream to access the fluid API (line 6). Then we call map, and pass a lookup function which calls findGuitaristByName (line 7).

Now we want to filter out any empty Optional values.

We call Stream.flatMap (different to Optional.flatMap) which takes a function which produces a Stream. In our case that’s Optional.stream. flatMap adds the elements contained within the resulting Stream to the original Stream (line 8).

Or in other words, we convert 3 Stream objects into a single Stream containing 2 elements.

With only 2 elements in the Stream, the final step is to convert it to a List (line 9).

8. Optional summary

We’ve covered a lot, so let’s summarise the most important points about Optional:

  • use Optional as the return type of a method which needs to return either a value or nothing
  • create an Optional of a specific type using the of or empty methods
  • when querying an Optional, use fluid APIs like ifPresent, orElse, and orElseThrow
  • avoid methods which could lead to error-prone code, like isPresent, isEmpty, and get
  • avoid using Optional for method parameters or class variables
  • more complex use cases are likely covered by advanced Optional APIs (see below)

Here are the 17 Optional methods we’ve covered in this article.

Optional methodsRecommended?Description
creation methods
ofCreates an Optional with a value.
emptyCreates an empty Optional.
ofNullable✅ (only if necessary)Can accept a null value. Creates either a present or empty Optional.
basic methods
isPresentReturns true if the Optional has a value.
isEmptyReturns true if the Optional is empty.
getReturns the Optional value or throws NoSuchElementException if empty.
functional methods
ifPresentCalls the provided function if the Optional has a value.
ifPresentOrElseCalls the provided function if the Optional has a value, otherwise calls the other function.
orElseReturns the value of the Optional, if present, otherwise returns the provided default value.
orElseGetReturns the value of the Optional, if present, otherwise returns the default value returned by the supplier function.
orElseThrowReturns the value of the Optional, if present, otherwise throws a NoSuchElementException.
orElseThrow (with supplier function)Returns the value of the Optional, if present, otherwise throws the exception returned by the supplier function.
orReturns the Optional, if present, otherwise, returns the Optional returned by the supplier function.
advanced methods
filterApplies a condition using the predicate function. If it returns true, then returns the Optional, otherwise returns an empty Optional.
mapIf a value is present, maps it to any other type using the function and wraps the returned value in an Optional. Otherwise returns an empty Optional.
flatMapIf a value is present, maps it to any other type of Optional using the function. Otherwise returns an empty Optional.
streamConverts the Optional to a Stream of length 0 or 1.

9. Next steps

Here are some resources you may find useful.

Why not also check out the accompanying YouTube video?

If you have any questions or suggestions, leave a comment down below or email me at tom@tomgregory.com.

Optional in Java: Everything You Need To Know

Leave a Reply

Your email address will not be published.

Scroll to top