Thought on Java 8 Optional
Introduction
When Java 8 came and I learned about Optional, I was quite excited and started
to use it everywhere for denoting absence of value. But I was quite surprised to
read the API Note from Java 9 docs.
Optionalis primarily intended for use as a method return type where there is a clear need to represent “no result,” and where using null is likely to cause errors. A variable whose type isOptionalshould never itself be null; it should always point to an Optional instance.
So Optional must be used as only return value. You shouldn’t accept Optional
as method parameter. Let’s explore the pitfalls and benefits in details.
Optional Misuse
Everybody had their own preference about programming and majority times perfect way of coding is just a matter of taste. But in my experience if you use the API the way developer intended, there is less surprises. All misuse of Optional boils down to 3 cases:
- Property
- Getter
- Method Parameter
Property
You can use Optional to denote null-able properties like below:
public class Person {
private Optional<String> additionalPhoneNumber;
// getters and setters
}But problem with this is java.util.Optional doesn’t implement
java.io.Serializable as it wasn’t intended to use as property. So when you
need to serialize data Optional property won’t be serialized. Although modern
libraries and frameworks can change this behaviors using reflection or byte code
generation, but you have to depend on that specific library. Mixing multiple
libraries with different philosophy (Optional property Serializable vs not
Serializable) can make the code complicated.
Getter
This is maybe tempting to declare nullable property normally and add a getter
with Optional. You are not violating any rule as documentation says it is only
intended to be used as return type.
public class Person {
private String additionalPhoneNumber;
public Optional<String> getAdditionalPhoneNumber() {
return Optional.ofNullable(this.additionalPhoneNumber);
}
}This code will work perfectly but tools like IDE or some code generator won’t
recognize this as getter. Java tools like JSP EL expression depend heavily on
naming convention. Those don’t support Optional getters (at least not yet).
You can declare another getter but that will also make code unnecessarily
complicated.
Method Parameter
Final misuse is to use Optional as method parameter. To be honest I find
following code more accessible then mentioning in documentation:
public void printGreetings(String name, Optional<String> salutation) {
String salutaionText = salutation.orElse("");
System.out.printf("Hello %s, Welcome!\n", salutationText + name);
}First problem is calling this method now becomes a bit difficult. We can’t call
with null and always have to wrap in Optional.
// Will throw NullPointerException
printGreetings("John", null);
// Need to wrap with Optional even we have non-empty String
printGreetings("John", Optional.of("Mr"));Another disadvantage using Optional here is Java tools already have better
support for solving this problem. Modern IDEs and libraries like Lombok has
support for annotation which can make code more readable.
import lombok.NotNull
public void printGreetings(@NotNull String name, String salutation) {
String salutaionText = Objects.nonNull(salutation) ? salutation : "";
System.out.printf("Hello %s, Welcome!\n", salutationText + name);
}Optional Usage
To take the full advantage of Optional it is mandatory to learn it’s API and
understand how it is intended to be used. Let’s first see how you can create
Optional objects.
import java.util.Optional;
// This will create an Optional with no value (Equivalent of null)
Optional<String> emptyTitle = Optional.empty();
// Use Optional.of() when you hava non-null value
Optional<String> titleOptional = Optional.of("Mr.");
Optional<String> titleOptional = Optional.of(null); // Will throw NullPointerException
// Use Optional.ofNullable() when value can be null
Optional<String> titleOptional = Optional.ofNullable("Mr.");
Optional<String> titleOptional = Optional.ofNullable(null); // Will create Optional.empty() object;Now for getting the value from Optional we have following method:
Optional<String> nameOptional = Optional.of("John");
String name = nameOptional.get();
System.out.println(name); // JohnBut we won’t always know weather Optional has value or is empty. Following
methods can query for existence of value:
if (nameOptional.isPresent()) {
System.out.println(nameOptional.get());
}
// Same code in functional style
nameOptional.ifPresent(System.out.println);Following methods are used when Optional doesn’t have value and we default
value for wants to throw exception
Optional<String> loggedInUserOptional = Optional.empty();
String loggedInUserName = loggedInUserOptional.orElse("Guest");
System.out.println(loggedInUserName); // Guest
// Similar to orElse() but takes a java.util.function.Supplier
String loggedInUserName = loggedInUserOptional.orElseGet(() -> "Guest " + getGuestNumber());
System.out.println(loggedInUserName); // Guest 1, when getGuestNumber() returns 1
// Following code will throw IllegalStateException immediately if optional is empty
loggedInUserOptional.orElseThrow(IllegalStateException::new);Filter is used to test for condition. It takes a java.util.function.Predicate
and returns empty Optional if condition doesn’t met.
Optional<User> user = findUserById(id);
if (author != null && user.isActive()) {
System.out.println("Name: " + user.getName());
}
// Same code using filter()
user.filter(User::isActive).ifPresent(user -> {
System.out.println("Name: " + user.getName());
});These methods helps to manipulate value inside Optional. Both of them are
similar but takes different kind of java.util.funciton.Function. Suppose I
want to makeOptional<String> uppercase both map() or flatMap() can be used
here.
Optional<String> nameOptional = Optional.of("John");
// Here map() takes [String -> Something] function
nameOptional.map(name -> name.toUpperCase()).ifPresent(System.out::println); // JOHN
// flatMap() takes [String -> Optional<Something>] function
// Same code written using flatMap()
nameOptional.flatMap(name -> Optional.of(name.toUpperCase())).ifPresent(System.out::println); // JOHNResource
- https://www.oracle.com/technical-resources/articles/java/java8-optional.html
- http://dolszewski.com/java/java-8-optional-use-cases
- https://blog.joda.org/2015/08/java-se-8-optional-pragmatic-approach.html
- https://stackoverflow.com/questions/31922866/why-should-java-8s-optional-not-be-used-in-arguments
- https://stackoverflow.com/questions/26327957/should-java-8-getters-return-optional-type