Optional Use cases
Null is a smell
- New class in
java.util package
- optional provides a means for a function returning a value to indicate the value could possibly be null.
- Optional is a box that hold at most one value, like Collections and Arrays, in it
- Optional is of 16 bytes, and is an
Object
. - creates a separate memory, excessive use should be avoided, as it can create performance issues.
- Optional is immutable. Once assigned, it cannot be reassigned.
Creating Optional - .of() vs .ofNullable()
Optional.of()
- Creates an Optional with a non-null value.
- If you pass null to this method, it throws a NullPointerException.
- Use Case: Use
Optional.of()
when you are certain that the value you are wrapping is not null.
Optional.ofNullable()
- Creates an Optional that can hold either a non-null value or null.
- If you pass null to this method, it creates an Optional that is empty (
**Optional.empty()**
). - Use Case: Use Optional.ofNullable() when the value you are wrapping might be null and you want to handle that gracefully.
Optional methods
Category | Methods of Optional | Description |
---|---|---|
Creating
Optional |
Optional.of() |
Optional with a non-null value |
Optional.ofNullable |
Optional that can hold either a non-null value or null | |
Unwrapping
Optionals |
optional.get()
|
returns value if present or throws
NoSuchElementException: No value present
|
optional.orElse(other) |
returns value if present or returns other | |
optional.orElseGet(Supplier) |
returns value if present or returns Supplier | |
apply function | optional.map(Function) |
returns an Option with function applied |
|
optional.isPresent() |
returns true if value is present |
optional.ifPresentOrElse(
value -> actionIfPresent(value),
() -> actionIfEmpty()
);
|
Beware of the Shared Mutability
|
|
String str = null;
// Create an Optional that may or may not have a value
Optional<String> optional = Optional.ofNullable(str);//Value expecting a String
System.out.println(optional.get());//NoSuchElementException: No value present
System.out.println(optional.orElse("other"));//other
System.out.println(optional.orElseGet(String::new));//EMPTY String
System.out.println(optional.isPresent());//false
IfPresentOrElse
-
If present then set else keep a default value
-
Considering a scenario where we need to return the first element, if present, else return an empty response.
Avoid ternary operator with Optional
Use of if
statement can be avoided using declarative functional style.
String str = (null != student.getFirstName()) ? student.getFirstName().toUpperCase() : StringUtils.EMPTY;
//is Equivalent to
String str = Optional.of(student.getFirstName().toUpperCase).orElse(StringUtils.EMPTY);
Optional with map() - Applying converters
With Optional, we get the advantage of applying function(map) as well.
//adding city name from the list obj and appending a comma if the city exist, else "NO_CITY"
String[] cities = {("New York"), (null), ("Los Angeles"), ("Chicago")};
for (String city : cities) {
// Using Map, avoiding ternary operator
String str2 = Optional.ofNullable(city)
.map(obj -> obj.toUpperCase() + ",")//Advantage of using map
.orElse("NO_CITY");
System.out.println(str2);
}
- More elaborate example
Patterns & Anti-patterns - Do’s and Dont’s
var has strict type checking. Reassignment has to match the type
initially
set.
var a = SampleData.getSimpleEmployees();
//a = "Nitin";//Strict type checking
Receive an optional
Use var
to obtain an optional from a service or a method
var result = getName();//returns an Optional
String str = result.orElse("not found");//Default Value
//Or str = result.orElseGet(String::new);//Empty String
//Or str = result.orElseThrow();//if at all you need to use get, then use orThrow instead
//str = result.get();//DO NOT USE THIS due to the danger on NPE
Fields
- There is no reason to use Optional as a field.
- use
optional.orElse()
instead ofoptional.get()
to retrieve a value into a field.- If there is really a need to use
get()
, useoptional.orElseThrow()
to know the real reason of blowing up
- If there is really a need to use
Method parameter
-
Do not use Optional
as a parameter to methods. If needed, use overloading instead. public static void methodName(Optional<String> name); //Anti-pattern - DO NOT DO THIS
- Optional in the argument will force/punish the programmers when the method is
invoked
methodName(Optional.empty());//Not Good //OR methodName(Optional.of(str));//Not Good
- Instead, use overloading
//A good design has empathy public static void methodName() { //use the default value } public static void methodName(String name) { //use the given name }
- Instead, use overloading
Method
Return Optional from a method to make it failsafe.
When we have a single value to return
- Instead of returning
null
(from method) returnOptional<T>
-
If a method always has a single value as a result, do not use Optional.
- In the given method, instead of returning a Map, returning an Optional of Map
provides more flexibility
public Optional<Map<String, Object >> getInfoByCode(String code){ Map<String, String> queryParams = new HashMap<>(); queryParams.put("excludeSensitiveInfo", "true"); //Get URL from config file List<Map<String, Object>> codes = restTemplate.getForObject(appConfig.getUrl()+"code/"+code, List.class, queryParams); //Returning a valid response even if the service fails. return Optional.ofNullable(Optional.of(codes.stream().findFirst().get()) .orElse(Collections.emptyMap())); }
- If a method may or may not have a single value as a result, then use
Optional.
public static Optional<String> getName() { if(fakeService.getRandNumber() < 3) { return Optional.of("Name"); } //return null; //ABSOLUTELY NO. it works but it's NASTY CODE return Optional.empty(); }
- If the result is a collection, then don’t use Optional, instead return an empty collection
With collections, Do not return a null, instead return an empty collection - Effective Java
The null == object
comparison
Using null == object
is a defensive programming practice,
where accidental assignment in conditionals can cause bugs.
Writing null == object
instead of object == null
prevents accidental
assignment if mistakenly use a single =
instead of ==
.
For example, if(object = null)
, the expression object = null
will always
evaluate to null,
and the if condition will always be false, even if the object is not null.
Handling Null pointer Exceptions (NPE)
Boolean Variables
Prefer primitive boolean types over Wrapper class to avoid creating objects & NPE.
If a String, intended to carry boolean values, you should use the
Boolean.parseBoolean(String)
method.
String trueString = "true";
// Parsing strings to boolean
boolean isTrue = Boolean.parseBoolean(trueString);
Misinterpretation of Argument: The argument for Boolean.getBoolean
should
be the name of a system property,
not a boolean value.
String isBooleanFlag = "true";//'undefined', 'false', 'anyStringValue '
//isBooleanFlag is intended to be a boolean value, this usage is incorrect
boolean aBoolean = Boolean.getBoolean(isBooleanFlag);
Removing ‘null’ values
// if any object in a list is NULL
list.stream()
.filter(object -> null != object) //Removing potential objects that might create exception
.map(str -> str.toLowerCase())
.collect(Collectors.toList());
Ensuring, in case there is a null return, the exception is handled properly
Order specificOrders = orders.stream()
.filter(order -> order.getOrderNumber().equals(electronicOrder.getOrderNumber()))
.findFirst()
.orElse(null);
if(null != specificOrders) {
...
...
...
}
Or same code optimized
orders.stream()
.filter(order -> order.getOrderNumber().equals(electronicOrder.getOrderNumber()))
.findFirst()
.ifPresent(specificOrders -> {
// Perform actions on specificOrders
...
...
...
});