Java's addition of java.util.Optional has been welcome and had led to more fluent code for methods that cannot always return non-null
values. Unfortunately, Optional has been abused and one type of abuse has been overuse. I occasionally have run across code that makes use of Optional when there is no clear advantage over using null
directly.
A red flag that can tip off when Optional
is being used for no advantage over checking for null
directly is when calling code employs Optional.ofNullable(T) against the returned value from the method it has just invoked. As with all "red flags," this doesn't mean it's necessarily a bad thing to pass the returned value from a method to Optional.ofNullable(T)
(in fact, it is necessary to pass to APIs expecting Optional
), but it is common for this approach to be used to provide no real value over using the returned value directly and checking it for null
.
Before Optional was available, code to check for null
returned from a method and acting one way for null
response and another way for non-null
response is shown next (all code snippets in this post are available on GitHub).
/** * Demonstrates approach to conditional based on {@code null} or * not {@code null} that is traditional pre-{@link Optional} approach. */ public void demonstrateWithoutOptional() { final Object returnObject = methodPotentiallyReturningNull(); if (returnObject == null) { out.println("The returned Object is null."); } else { out.println("The returned object is NOT null: " + returnObject); // code processing non-null return object goes here ... } }
For this basic conditional, it's rarely necessary to involve Optional
. The next code snippet is representative of the type of code I've occassionally seen when the developer is trying to replace the explicit null
detection with use of Optional:
/** * Demonstrates using {@link Optional} in exactly the manner {@code null} * is often used (conditional on whether the returned value is empty or * not versus on whether the returned value is {@code null} or not). */ public void demonstrateOptionalUsedLikeNullUsed() { final Optional<Object> optionalReturn = Optional.ofNullable(methodPotentiallyReturningNull()); if (optionalReturn.isEmpty()) { out.println("The returned Object is empty."); } else { out.println("The returned Object is NOT empty: " + optionalReturn); // code processing non-null return object goes here ... } }
The paradigm in this code is essentially the same as the traditional null
-checking code, but uses Optional.isEmpty() to perform the same check. This approach does not add any readability or other advantage but does come at a small cost of an additional object instantiation and method call.
A variation of the above use of Optional
is to use its ifPresent(Consumer) method in conjunction with its isEmpty() method to form the same basic logic of doing one thing if the returned value is present and another thing if the returned value is empty. This is demonstrated in the following code.
/** * Demonstrates using {@link Optional} methods {@link Optional#ifPresent(Consumer)} * and {@link Optional#isEmpty()} in similar manner to traditional condition based * on {@code null} or not {@code null}. */ public void demonstrateOptionalIfPresentAndIsEmpty() { final Optional<Object> optionalReturn = Optional.ofNullable(methodPotentiallyReturningNull()); optionalReturn.ifPresent( (it) -> out.println("The returned Object is NOT empty: " + it)); if (optionalReturn.isEmpty()) { out.println("The returned object is empty."); } }
This code appears bit shorter than the traditional approach of checking the returned value directly for null
, but still comes at the cost of an extra object instantiation and requires two method invocations. Further, it just feels a bit weird to check first if the Optional is present and then immediately follow that with a check if it's empty. Also, if the logic that needed to be performed was more complicated than writing out a message to standard output, this approach becomes less wieldy.
Conclusion
Code that handles a method's return value and needs to do one thing if the returned value is null
and do another thing if the returned value is not null
will rarely enjoy any advantage of wrapping that returned value in Optional
simply to check whether it's present or empty. The wrapping of the method's returned value in an Optional
is likely only worth the costs if that Optional
is used within fluent chaining or APIs that work with Optional
.
6 comments:
I think this might be at a corner case, maybe dealing with void or using print like statements?
Using Optional methods this example can be succinctly written as:
class NullOrOptional {
Object methodReturningNull() {
return null;
}
Object methodNotReturningNull() {
return new Object();
}
}
final NullOrOptional maybe = new NullOrOptional();
// This is basically the replacement for the if ... then ...
out.println("Verdict: " + Optional.ofNullable(maybe.methodReturningNull())
.map(value -> "Not null: " + value)
.orElse("The returned Object is null"));
Being unfamiliar using the Optional and more familiar with if{} then{} can make the above look initially odd.
It is still probably true that "... Optional Does Not Supplant All Traditional if-null-else or if-not-null-else Checks", but I think it might be a rarity.
I've seen examples like Rod's in real code:
Optional.ofNullable(someMethod()).orElse(someDefaultValue);
While it's succinct, I think it unnecessarily creates an object when you could instead use the following:
Objects.requireNonNullElse(someMethod(), someDefaultValue);
It pays to watch some of the API changes from one version to the next. There are often little gems like Objects.requireNonNullElse() that may go unnoticed.
I really like Optionals (although Optional.isPresent + Optional.get() give me the creeps, too), and I don't have a problem with the code example
Optional.ofNullable(someMethod()).orElse(someDefaultValue); (whether a solution creates an extra option or not is unimportant for my typical use cases).
However, I agree with Rob McDougall and his recommendation to check API changes. I've gotten a lot of mileage out of Objects.requireNonNullElse(). One of my favorites is:
if (!Objects.requireNonNullElse(someString, "").isEmpty() ) { ... }
Method ifPresentOrElse() was added in Java 9.
Sample Program:
// Java program to demonstrate
// Optional.ifPresentOrElse() method
import java.util.*;
public class GFG {
public static void main(String[] args)
{
// create a Optional
Optional op = Optional.of(9455);
// print value
System.out.println("Optional: " + op);
// apply ifPresentOrElse
op.ifPresentOrElse(
(value) -> { System.out.println("Value is present, its: "+ value); },
()-> { System.out.println("Value is empty"); });}
}
Output:
Optional: Optional[9455]
Value is present, its: 9455
Post a Comment