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:
-
contains an item (also known as present)
-
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.
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();
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
.
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:
-
the method signature returns
Optional<Guitarist>
, or in other words anOptional
of typeGuitarist
-
if
lastName
is Hendrix, we construct a Guitarist object and pass it toOptional.of
. This creates anOptional
containing theGuitarist
, which we return from the method. -
if
lastName
is anything else, we returnOptional.empty()
. This creates an emptyOptional
, 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:
-
contains a value
-
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.
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 substitutes 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 supplier 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
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.
-
an
Optional
with a value -
an empty
Optional
-
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());
}
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);
}
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:
-
isPresent()
returnstrue
if theOptional
contains a value -
get()
returns the value contained within theOptional
, or throwsNoSuchElementException
if theOptional
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 explicitly 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 returns either a value or nothing -
create an
Optional
of a specific type using theof
orempty
methods -
when querying an
Optional
, use fluid APIs likeifPresent
,orElse
, andorElseThrow
-
avoid methods leading to error-prone code e.g.
isPresent
,isEmpty
, andget
-
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 methods |
Recommended? | Description |
---|---|---|
creation methods | ||
of | β | Creates an Optional with a value. |
empty | β | Creates an empty Optional . |
ofNullable | β (only if necessary) | Can accept a null value. Creates either a present or empty Optional . |
basic methods | ||
isPresent | β | Returns true if the Optional has a value. |
isEmpty | β | Returns true if the Optional is empty. |
get | β | Returns the Optional value or throws NoSuchElementException if empty. |
functional methods | ||
ifPresent | β | Calls the provided function if the Optional has a value. |
ifPresentOrElse | β | Calls the provided function if the Optional has a value, otherwise calls the other function. |
orElse | β | Returns the value of the Optional , if present, otherwise returns the provided default value. |
orElseGet | β | Returns the value of the Optional , if present, otherwise returns the default value returned by the supplier function. |
orElseThrow | β | Returns 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. |
or | β | Returns the Optional , if present, otherwise, returns the Optional returned by the supplier function. |
advanced methods | ||
filter | β | Applies a condition using the predicate function. If it returns true , then returns the Optional , otherwise returns an empty Optional . |
map | β | If 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 . |
flatMap | β | If a value is present, maps it to any other type of Optional using the function. Otherwise returns an empty Optional . |
stream | β | Converts the Optional to a Stream of length 0 or 1. |
9. Next steps
Here are some resources you may find useful.
-
GitHub repository where you can try out many of the examples from this article
-
JDK 18 API documentation for
Optional
Stop reading Gradle articles like these
This article helps you fix a specific problem, but it doesn't teach you the Gradle fundamentals you need to actually help your team succeed.
Instead, follow a step-by-step process that makes getting started with Gradle easy.