8 minutes
Optional in Java
Java 8 has some cool features introduced on it’s release, for example, lamda, method reference etc. One of the major features is Optional. Today, we will discuss what optional is, why it is introduced & basic use case of it.
The Billion Dollar Mistake
Tony Hoare, developer of quicksort algorithm, invented null reference in 1965. In 2009, he described it as a Billion Dollar Mistake & apologized for inventing it. Now what is Null Reference & why is it called The Billion Dollar Mistake?
According to wikipedia,
In computing, a null pointer or null reference is a value saved for indicating that the pointer or reference does not refer to a valid object.
If you are familier with java or any of its close cousins, you should read out the following example easily:
String name = null;
System.out.println(name.length());
Can you guess what the output here will be?
Yes, you are right, it will throw the infamous NullPointerException
here.
Because name
is not initialized here & we are calling String.length()
on it without check! So, the null safe solution of this issue is,
String name = null;
if(name != null) {
System.out.println(name.length());
}
Now why I have reffered NullPointerException as infamous exception you may ask. Because it is very common senario. If you have some experience with any mordern programming language (especially java), you probably encountered with it before. It’s so common, for some cases, you might write hundreds of null checking blocks, still may miss some cases. Again, we programmers are forgetful in nature, we miss the null check all the time, we love to test our code with the happy case senario only & ship it to production. And in production, our piece of code faces-off against unwanted null reference and throws nullPointerException.
Null Safety & Optional
Mordern languages, for example Kotlin provides Null Safety (? operator), to solve the null-reference problem. For example, previous problem can be solved in kotlin like,
var name : String? = null
name?.let {
print(it)
}
Null Safety in Kotlin using Safe Call Operator (?.
) is very easy to use & understandable.
Sadly java still doesn’t have any operator like the Kotlin’s safe call operator. Instead Java-8 introduced Optional wrapper class. Optional is a final class under the java.util package. It is a generic class which actually wraps around the actual value & provides the null reference safety & some additional operations regarding the null safety. Let’s solve the problem with java optional.
Optional<String> nameOptional = Optional.empty();
nameOptional.ifPresent(name -> System.out.println(name.length()));
What The Heck!!! why are we using optional here for?
The vanilla java solution (the if(name != null)
check solution) is more simple and easy to understand solution.
What is the benifit to use optional instead?
The reason is my friend to forcefully remind us the issue.
The if (object != null)
checking solution is all perfect; no doubt about it.
But that solution is valid when we are not forgetful.
Suppose we have two blocks of code where the can-be-null object is used, but forget to check null in one of the blogs instead; then the null safety is guaranteed again.
String name = null;
if(name != null) {
System.out.println(name.length());
}
....
....
....
Integer k = name.length() + 1;
But if we wrap the object with optional, we must check for null everytime we need to use the object. It is very tedious I know, but better secured.
Optional in Java
Now, we will see how we can create new optional & some use-cases of it.
-
Creating Optional
Optional provides some static method which can be used as it’s builder methods.
-
To create an empty optional, we can use
Optional.empty()
static method. Empty optional means that the wrapped object has no valid value.Optional<String> nameOptional = Optional.empty();
-
If any object has value for sure, we can wrap the object with optional using
Optional.of(...)
static method. It takes the object as param. When usingof
static method, we must be sure that object can’t be null, if it becomes null for any case,of
method will throwNullPointerException
.Optional<String> nameOptional = Optional.of("Altair");
-
If any object has value or is null, we can wrap the object with optional using
Optional.ofNullable(...)
static method. It takes the object-in-question as param.Optional<String> invalidNameOptional = Optional.ofNullable(null); Optional<String> validNameOptional = Optional.ofNullable("Altair");
-
-
Checking Optional’s value
- If any Optional object has value present in it,
Optional.isPresent()
method will returntrue
, otherwisefalse
.Optional<String> nameOptional = Optional.of("Altair"); if (nameOptional.isPresent()) { System.out.println("This message should be printed."); }
- As revert of isPresent method,
Optional.isEmpty()
method will returntrue
when optional object has no value in it, otherwisefalse
.Optional<String> nameOptional = Optional.empty(); if (nameOptional.isEmpty()) { System.out.println("name is empty. This message should be printed."); }
- If any Optional object has value present in it,
-
Unwrapping Optional’s value
If any
Optional
is not empty, we can unwrap the Optional’s Object usingOptional.get()
method.Optional<String> nameOptional = Optional.of("Altair"); String name = nameOptional.get(); Assert.assertEquals(name, "Altair");
-
Conditional blocks
-
If we execute some logic whenever an Optional is not empty, we can use
Optional.ifPresent(...)
method, which takes aConsumer
as param, which is invoked only when Optional has value.Optional<String> nameOptional = Optional.of("Altair"); nameOptional.ifPresent(name -> { System.out.println(name); });
-
When Optional is empty, we want to unwrap the Optional and pass an object as default value, we can use
Optional.orElse(...)
. This method takes an object which is the default value that we want to pass if the optional is empty.Optional<String> nameOptional = Optional.empty(); String name = nameOptional.orElse("anonymous"); Assert.assertEquals(name, "anonymous");
-
Optional.orElseGet(...)
is similar toorElse(...)
method; the main difference isorElse()
takes an object which will be returned if the the Optional is empty, andorElseGet()
takes aSupplier
interface as param, which will only be invoked when the Optional is empty.As
orElse
method directly returns the object provided to it, the default object should always be initiated before-hand when we use this. On the other hand, the default object only be created when Optional is empty when usingorElseGet
. That’s why, when we deal with some IO operation / heavy Object (expensive object to be initiated) as default value, it is better to useorElseGet
instead oforElse
.Optional<String> nameOptional = Optional.empty(); String name = nameOptional.orElseGet(() -> "anonymous"); Assert.assertEquals(name, "anonymous");
-
Optional.orElseThrow(...)
is similar toorElseGet
, the difference is instead of returning default value when Optional is empty,orElseThrow
will throw an exception. It takes a supplier as param which, provides the exception to be thrown.Optional<String> nameOptional = Optional.empty(); String name = nameOptional.orElseThrow(CustomException::new);
java 10 also has a no-arg
orElseThrow
method, which throwsNoSuchElelementException
.Optional<String> nameOptional = Optional.empty(); String name = nameOptional.orElseThrow();
-
From Java 9, there is a new method called
Optional.or(...)
, which is similar toorElseGet
as functionality but default in return type. Where,orElse
andorElseGet
unwrapped the optional object,or
method doesn’t unwrap & return theOptional<Type>
, so that we can perform further Optional related actions on it.Optional<String> nameOptional = Optional.empty(); Optional<String> anotherOptional = nameOptional.or(() -> "anonymous"); Assert.assertEquals(anotherOptional.get(), "anonymous");
-
If Optional not empty, do this… else, do that
This is a common scenerio of Optional. To cover this flow into lamda, Java 9 introduced
ifPresentOrElse(...)
method. It takes Consumer which is invoked when optional in not empty, and Runnable as second param which’srun
method is invoked when the optional is empty.Optional<String> nameOptional = Optional.of("Altair"); nameOptional.ifPresentOrElse(name -> System.out.println(name), () -> System.out.println("No name found!!!"));
-
-
Filtering & Mapping
-
To test and filter out value, we can use
Optional.filter(...)
method. It takesPredicate
as param and return Optional. If Optional is not empty, the pedicate’s test method is invoked. If test method return true, filter method will return theOptional<value>
, otherwise returnOptional.empty()
.Optional<String> nameOptional = Optional.of("Altair").filter(name -> name.equals("John")); Assert.assertEquals(nameOptional.isPresent(), false);
-
For non-empty Optional,
Optional.map(...)
method transform the value to another value. It takesFunction
as param.Function
’sapply
method takes the optional’s value and returned new object of the expected type.Optional<String> nameOptional = Optional.of("Altair"); Optional<Integer> lengthOptional = nameOptional.map(String::length);
-
Optional.flatMap(...)
is same asOptional.map(...)
. The difference is map’s Function.apply method takes unwrapped value; where flatMap takes Optional wrapped value, unwrap it first, then transfrom it.Suppose we have the following,
public class Hero { private String name; public Hero(String name) { this.name = name; } public Optional<String> getName() { return Optional.ofNullable(name); } }
Here, want A hero’s name’s length as optional. If we use
map
method here:Optional<Hero> heroOptional = Optional.of(new Hero("Altair")); Optional<Optional<String>> nameOptionalWrapper = heroOptional.map(Hero::getName); Optional<String> nameOptional = nameOptionalWrapper.orElse(Optional.empty()); Optional<Integer> nameLengthOptional = nameOptional.map(String::length);
In this case, if we use
flatMap
instead:Optional<Hero> heroOptional = Optional.of(new Hero("Altair")); Optional<String> nameOptional = heroOptional.flatMap(Hero::getName); Optional<Integer> nameLengthOptional = nameOptional.map(String::length);
-
Java 9 introduced
Optional.stream()
method. It treats optional instance as aStream
. So we can use variousStream API
functionality for optional, such as collect, map, filter, flatMap etc.List<String> nameList = Optional.of("Altair").stream().collect(Collectors.toList());
-
Conclusion
Optional is one of the powerful tools of Java’s arsenal. We can construct complex logic by chaining various optionals functionalities together as it empathizes Stream API flow. But, we need to be careful with using optional. Using optional in unnecessary places can create some boiler-plate code. Again optional is not meant to used as method param, because Optional Wrapper can be null just like any other java object. So stick it in your mind,
Use Optional as method return type, Never use Optional as method’s param type.