There are many articles that explain the broad applications of Java 8’s new Optional
class (e.g., Tired of Null Pointer Exceptions? Consider Using Java SE 8’s Optional
!), but they focus on how authors can use Optional
to improve the design of their own APIs. I want to talk about how Optional
can be used to protect us from the APIs we didn’t write.
One thing we can mitigate are methods that usually return a Collection
(or array), but will instead return null
when there are no items to put into the Collection
—ideally, they would return an empty Collection
. I deal with this most often when calling HttpServletRequest#getCookies()
which returns “an array of all the Cookies included with this request, or null if the request has no cookies”.
What I used to do in this situation is this:
// Sub-optimal way to handle null return value.
// Don't do this.
Cookie[] cookies = request.getCookies();
if (cookies != null)
{
// Go about your business...
}
But that null
check is bumming me out! We can use Optional
here to handle the null
case for us:
// Optional makes it better.
// Use this.
Cookie[] cookies = Optional
.ofNullable(request.getCookies())
.orElse(new Cookie[] {});
// Go about your business...
Just to make the point: suppose the return value of getCookies()
was Set
, for example, then the argument of orElse
would be Collections.emptySet()
.
This makes things more elegant when we stream
the array. For example, we can look for the value of a particular Cookie
like so:
// Elegant.
// Use this.
String cookieValue
= Arrays.stream(Optional
.ofNullable(request.getCookies())
.orElse(new Cookie[] {}))
.filter(cookie
-> "cookieName".equals(cookie.getName()))
.map(Cookie::getName)
.findFirst() // <-- another Optional
.orElse("default");
The above code snippet is better than the next, wouldn’t you say?
// Ugly null check, boo!
// Don't use this.
Cookie[] cookies = request.getCookies();
if (cookies != null)
{
String cookieValue
= Arrays.stream(request.getCookies())
.filter(
cookie -> "cookieName".equals(cookie.getName()))
.map(Cookie::getName)
.findFirst() // <-- another Optional
.orElse("default");
}