DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • Introducing Graph Concepts in Java With Eclipse JNoSQL
  • Using Java Stream Gatherers To Improve Stateful Operations
  • How to Merge HTML Documents in Java
  • The Future of Java and AI: Coding in 2025

Trending

  • Customer 360: Fraud Detection in Fintech With PySpark and ML
  • Mastering Advanced Aggregations in Spark SQL
  • MySQL to PostgreSQL Database Migration: A Practical Case Study
  • DGS GraphQL and Spring Boot
  1. DZone
  2. Coding
  3. Java
  4. Optional in Java 8 Cheat Sheet

Optional in Java 8 Cheat Sheet

java.util.Optional in Java 8 is a poor cousin of scala.Option[T] and Data.Maybe in Haskell. But this doesn’t mean it’s not useful.

By 
Tomasz Nurkiewicz user avatar
Tomasz Nurkiewicz
DZone Core CORE ·
Aug. 19, 13 · Tutorial
Likes (7)
Comment
Save
Tweet
Share
73.7K Views

Join the DZone community and get the full member experience.

Join For Free

java.util.Optional<T> in Java 8 is a poor cousin of scala.Option[T] andData.Maybe in Haskell. But this doesn’t mean it’s not useful. If this concept is new to you, imagine :

Optional

 as a container that may or may not contain some value. Just like all references in Java can point to some object or be 

null

, 

Option

 may enclose some (non-null!) reference or be empty.


Turns out that the analogy between 

Optional

 and nullable references is quite sensible. 

Optional

 was introduced in Java 8 so obviously it is not used throughout the standard Java library - and never will be for the backward compatibility reasons. But I recommend you at least giving it a try and using it whenever you have nullable references. 

Optional

 instead of plain 

null

 is statically checked at compile time and much more informative as it clearly indicates that a given variable may be present or not. Of course it requires some discipline - you should never assign 

null

 to any variable any more.


Usage of option (maybe) pattern is quite controversial and I am not going to step into this discussion. Instead I present you with few use-cases of 

null

 and how they can be retrofitted to 

Optional<T>

. In the following examples given variables and types are used:


public void print(String s) {
System.out.println(s);
}
String x = //...
Optional<String> opt = //...
x

 is a String that may be 

null

, 

opt

 is never 

null

, but may or may not contain some value (present or empty). There are few ways of creating 

Optional

:

opt = Optional.of(notNull);
opt = Optional.ofNullable(mayBeNull);
opt = Optional.empty();

In the first case 

Optional

 must contain not 

null

 value and will throw an exception if 

null

 is passed. 

ofNullable()

 will either return empty or present (set) 

Optional

. 

empty()

 always return empty 

Optional

, corresponding to 

null

. It’s a singleton because 

Optional<T>

 is immutable.

ifPresent() - do something when Optional is set

Tedious 

if

 statement:

if (x != null) {
print(x);
}

can be replaced with higher-order function 

ifPresent()

:

opt.ifPresent(x -> print(x));
opt.ifPresent(this::print);

The latter syntax (method reference) can be used when lambda argument (

String x

) matches function formal parameters.


filter() - reject (filter out) certain Optional values.

Sometimes you want to perform some action not only when a reference is set but also when it meets certain condition:


if (x != null && x.contains("ab")) {
print(x);
}

This can be replaced with 

Optional.filter()

 that turns present (set) 

Optional

 to empty 

Optional

 if underlying value does not meet given predicate. If input 

Optional

 was empty, it is returned as-is:


opt.
filter(x -> x.contains("ab")).
ifPresent(this::print);

This is equivalent to more imperative:


if(opt.isPresent() && opt.get().contains("ab")) {
print(opt.get());
}

map() - transform value if present

Very often you need to apply some transformation on a value, but only if it’s not 

null

 (avoiding 

NullPointerException

):


if (x != null) {
String t = x.trim();
if (t.length() > 1) {
print(t);
}
}

This can be done in much more declarative way using 

map()

:


opt.
map(String::trim).
filter(t -> t.length() > 1).
ifPresent(this::print);

This becomes tricky. 

Optional.map()

 applies given function on a value inside 

Optional

 - but only if 

Optional

 is present. Otherwise nothing happens and 

empty()

 is returned. Remember that the transformation is type-safe - look at generics here:


Optional<String>  opt = //...
Optional<Integer> len = opt.map(String::length);

If 

Optional<String>

 is present 

Optional<Integer> len

 is present as well, wrapping length of a 

String

. But if 

opt

was empty, 

map()

 over it does nothing except changing generic type.


orElse()/orElseGet() - turning empty Optional<T> to default T

At some point you may wish to unwrap 

Optional

 and get a hold of real value inside. But you can’t do this if 

Optional

 is empty. Here is a pre-Java 8 way of handling such scenario:


int len = (x != null)? x.length() : -1;

With 

Optional

 we can say:


int len = opt.map(String::length).orElse(-1);

There is also a version that accepts Supplier<T> if computing default value is slow, expensive or has side-effects:


intlen = opt.
map(String::length).
orElseGet(() -> slowDefault());  //orElseGet(this::slowDefault)

flatMap() - we need to go deeper

Imagine you have a function that does not accept 

null

 but may produce one:


public String findSimilar(@NotNullString s) //...

Using it is a bit cumbersome:


String similarOrNull = x != null? findSimilar(x) : null;

With 

Optional

 it is a bit more straighforward:


Optional<String> similar = opt.map(this::findSimilar);

If the function we 

map()

 over returns 

null

, the result of 

map()

 is an empty 

Optional

. Otherwise it’s the result of said function wrapped with (present) 

Optional

. So far so good but why do we return 

null

-able value if we have 

Optional

?


public Optional<String> tryFindSimilar(String s)  //...

Our intentions are clear but using 

map()

 fails to produce correct type. Instead we must use 

flatMap()

:


Optional<Optional<String>> bad = opt.map(this::tryFindSimilar);
Optional<String> similar =  opt.flatMap(this::tryFindSimilar);

Do you see double 

Optional<Optional<...>>

? Definitely not what we want. If you are mapping over a function that returns 

Optional

, use 

flatMap

 instead. Here is a simplified implementation of this function:


public<U> Optional<U> flatMap(Function<T, Optional<U>> mapper) {
if(!isPresent())
returnempty();
else{
returnmapper.apply(value);
}
}

orElseThrow() - lazily throw exceptions on empty Optional

Often we would like to throw an exception if value is not available:


public char firstChar(String s) {
if(s != null&& !s.isEmpty())
returns.charAt(0);
else
throw new IllegalArgumentException();
}

This whole method can be replaced with the following idiom:


opt.
filter(s -> !s.isEmpty()).
map(s -> s.charAt(0)).
orElseThrow(IllegalArgumentException::new);

We don’t want to create an instance of exception in advance because creating an exception has significant cost.


Bigger example

Imagine we have a 

Person

 with an 

Address

 that has a 

validFrom

 date. All of these can be 

null

. We would like to know whether 

validFrom

 is set and in the past:


private boolean validAddress(NullPerson person) {
if(person != null) {
if(person.getAddress() != null) {
finalInstant validFrom = person.getAddress().getValidFrom();
returnvalidFrom != null&& validFrom.isBefore(now());
} else
return false;
} else
return false;
}

Quite ugly and defensive. Alternatively but still ugly:


returnperson != null&&
person.getAddress() != null&&
person.getAddress().getValidFrom() != null&&
person.getAddress().getValidFrom().isBefore(now());

Now imagine all of these (

person

, 

getAddress()

, 

getValidFrom()

) are 

Optional

s of appropriate types, clearly indicating they may not be set:


class Person {
private final Optional<Address> address;
public Optional<Address> getAddress() {
return address;
}
//...
}
class  Address {
private final Optional<Instant> validFrom;
public Optional<Instant> getValidFrom() {
return validFrom;
}
//...
}

Suddenly the computation is much more streamlined:


returnperson.
flatMap(Person::getAddress).
flatMap(Address::getValidFrom).
filter(x -> x.before(now())).
isPresent();

Is it more readable? Hard to tell. But at least it’s impossible to produce 

NullPointerException

 when 

Optional

 is used consistently.


Converting Optional<T> to List<T>

I sometimes like to think about 

Optional

 as a collection1 having either 0 or 1 elements. This may make understanding of

map()

 and 

flatMap()

 easier. Unfortunately 

Optional

 doesn’t have 

toList()

 method, but it’s easy to implement one:


public static<T> List<T> toList(Optional<T> option) {
return option.
map(Collections::singletonList).
orElse(Collections.emptyList());
}

Or less idiomatically:


public static<T> List<T> toList(Optional<T> option) {
if(option.isPresent())
return Collections.singletonList(option.get());
else
return Collections.emptyList();
}

But why limit ourselves to 

List<T>

? What about 

Set<T>

 and other collections? Java 8 already abstracts creating arbitrary collection via Collectors API, introduced for Streams. The API is hideous but comprehensible:


public static<R, A, T> R collect(Optional<T> option, Collector<? superT, A, R> collector) {
finalA container = collector.supplier().get();
option.ifPresent(v -> collector.accumulator().accept(container, v));
return collector.finisher().apply(container);
}

We can now say:


import static java.util.stream.Collectors.*;
List<String> list = collect(opt, toList());
Set<String>  set  = collect(opt, toSet());

Summary

Optional<T>

 is not nearly as powerful as 

Option[T]

 in Scala (but at least it doesn’t allow wrapping null). The API is not as straightforward as 

null

-handling and probably much slower. But the benefit of compile-time checking plus readability and documentation value of 

Optional

 used consistently greatly outperforms disadvantages. Also it will probably replace nearly identical com.google.common.base.Optional<T> from Guava


1 - from theoretical point of view both maybe and sequence abstractions are monads, that’s why they share some functionality

Java (programming language)

Published at DZone with permission of Tomasz Nurkiewicz, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Introducing Graph Concepts in Java With Eclipse JNoSQL
  • Using Java Stream Gatherers To Improve Stateful Operations
  • How to Merge HTML Documents in Java
  • The Future of Java and AI: Coding in 2025

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!