Before Java 8 there were only two possible ways to gracefully exit a method when a value is not present, either you return null
or throw an exception. This has changed since Java 8 with the Optional
interface.
null
: efficient, but risky (we have to make sure that the client is going to handle a possiblenull
return value).Exception
: elegant, but expensive (the stack trace is captured).
Java 8 includes the class
java.util.Optional
out-of-the-box.
Optional Class
Since Java 8 we have a third alternative, Optionals, which can contain an element or nothing. Optional
allows us to return an empty result.
The optional library provides two ways to return an empty Optional
: Optional.Empty()
and Optional.ofNullable(nullValue)
. But do not use Optional.of(value)
if a null value is expected to be returned, because in that case it will throw a NullPointerException
.
public class MyClass {
public static Optional<Integer> findMyLuckyNumber(List<Integer> list, Integer luckyNumber) {
if (list.isEmpty()) {
return Optional.empty();
}
Integer result = null;
for (Integer value : list) {
if (value == luckyNumber ) {
result = value;
}
}
return Optional.ofNullable(result);
}
}
private static final Logger LOGGER = Logger.getLogger(Main.class.getName());
public static void main(String[] args) {
// returns Optional with value
LOGGER.info(MyClass.findMyLuckyNumber(List.of(2, 5, 9), 9).toString());
// returns empty Optional
LOGGER.info(MyClass.findMyLuckyNumber(List.of(2, 5, 9), 1).toString());
// returns empty Optional
LOGGER.info(MyClass.findMyLuckyNumber(new ArrayList<>(), 1).toString());
}
Should I always used Optional instead of returning null or throwing an exception? In most of the cases it is recommended to return an Optional
, because returning null
or throwing an unchecked exception might be ignored by the client, and returning a checked exception forces the client side to write more code. Therefore, if an Optional
is returned, the client can decide what to do.
A default behavior can also be specified when null
is returned by appending .orElse(“Empty…”)
or orElseThrow(Exception::new)
. Another alternative is to use .orElseGet(Supplier)
, which is more efficient than .orElse()
, because the value is only retrieved when necessary. Remember that all these options return unwrapped values. Also you can use ifPresent()
to run a block of code if the value is present.
Java 9 added three new methods into the Optional
class: .or(Supplier<T>)
, ifPresentOrElse(Consumer, Runnable)
and the stream method, which allows you to use Optionals as streams. The main advantage of the first two methods is that now we can return an Optional
as default value.
Optional<Integer> defaultValue = Optional.of(99);
Optional<Integer> emptyValue = Optional.ofNullable(null);
// prints 99
System.out.println(emptyValue.or(() -> defaultValue).get());
You can also check if an Optional
is empty by calling isPresent()
, but since we have the methods mentioned above, you would rather use those for simplicity.
Drawbacks
Returning an Optional
is not always the best solution:
- For those cases when a list needs to be returned, it is better to just return an empty list.
- Optionals require to be allocated and initialized.
- For primitives values the
Optional
class cannot be use, instead you have to useOptionalInt
,OptionalLong
orOptionalDouble
. - You should not use Optionals as keys, values or elements in collections or arrays.
Conclusion
- Return null or Exception when performance is priority.
- Only use Optional as a return value.
- Optional fields are sometimes considered as code smell, because maybe there should be a subclass containing the optional values.