Mohamed Taman
Chief Solutions Architect, Owner/CEO of SiriusXI, a Java Champion, Oracle ACE, JCP member, Consultant, Speaker, and Author.
Mohamed Taman, Chief Solutions Architect
CEO @SiriusXI | Java Champion | Oracle ACE Alumni| Jakarta EE Ambassador| Author | Speaker.
25+ recipes to use Optional class effectively which is not optional.
@_tamanm
Or Google 🕵 me
"Mohamed Taman"
Agenda
QUESTION #1
CODE RECIPE #1
public Optional<Employee> getEmployee() {
Optional<Employee> employee = null;
...
}
//Avoid
CODE RECIPE #1.1
public Optional<Employee> getEmployee() {
Optional<Employee> employee = Optional.empty();
...
}
Since Java 8
//Prefer
// this is prone to be empty
Optional<Employee> employee = HRService.getEmployee();
/*
if "Employee" is empty then this code
will throw a java.util.NoSuchElementException
*/
Employee myEmployee = employee.get();
CODE RECIPE #2
//Avoid
Since Java 8
if (employee.isPresent()) {
Employee myEmployee = employee.get();
... // do something with "myEmployee"
} else {
... // do something that doesn't call employee.get()
}
Since Java 8
CODE RECIPE #2.1
//Prefer
public void callDynamicMethod(MyClass clazz, String methodName) throws ... {
// contains an instance of MyClass or empty if "myMethod" is static
Optional<myclass> myClass = clazz.getInstance();
Method = MyClass.class.getDeclaredMethod(methodName, String.class);
if (myClass.isPresent()) {
method.invoke(myClass.get(), "Test");
} else {
method.invoke(null, "Test");
}
}
CODE RECIPE #3
//Avoid
Method myMethod = ... ;
...
// contains an instance of MyClass or empty if "myMethod" is static
Optional<MyClass> myClassInstance = ... ;
...
myMethod.invoke(myClassInstance.orElse(null), ...);
Since Java 8
//Prefer
CODE RECIPE #3.1
QUESTION #2
public static final String USER_STATUS = "UNKNOWN";
...
public String getUserStatus(long id) {
Optional<String> status = ... ; // prone to return an empty Optional
if (status.isPresent()) {
return status.get();
} else {
return USER_STATUS;
}
}
CODE RECIPE #4
//Avoid
public static final String USER_STATUS = "UNKNOWN";
public String getUserStatus(long id) {
Optional<String> status = ... ; // prone to return an empty Optional
return status.orElse(USER_STATUS);
}
//Prefer
CODE RECIPE #4.1
public String computeStatus() {
... // some code used to compute status
}
public String getUserStatus(long id) {
Optional<String> status = ... ; // prone to return an empty Optional
// computeStatus() is called even if "status" is not empty
return status.orElse(computeStatus());
}
CODE RECIPE #5
//Avoid
public String computeStatus() {
... // some code used to compute status
}
public String getUserStatus(long id) {
Optional<String> status = ... ; // prone to return an empty Optional
if (status.isPresent()) {
return status.get();
} else {
return computeStatus();
}
}
//Avoid
CODE RECIPE #5.1
public String computeStatus() {
... // some code used to compute status
}
public String getUserStatus(long id) {
Optional<String> status = ... ; // prone to return an empty Optional
// computeStatus() is called only if "status" is empty
return status.orElseGet(this::computeStatus);
}
//Prefer
CODE RECIPE #5.2
Since Java 8
public String getUserStatus(long id) {
Optional<String> status = ... ; // prone to return an empty Optional
if (status.isPresent()) {
return status.get();
} else {
throw new NoSuchElementException();
}
}
CODE RECIPE #6
//Avoid
public String getUserStatus(long id) {
Optional<String> status = ... ; // prone to return an empty Optional
return status.orElseThrow();
}
//Prefer
Since Java 10
CODE RECIPE #6.1
public String getUserStatus(long id) {
Optional<String> status = ... ; // prone to return an empty Optional
if (status.isPresent()) {
return status.get();
} else {
throw new IllegalStateException();
}
}
CODE RECIPE #7
//Avoid
public String getUserStatus(long id) {
Optional<String> status = ... ; // prone to return an empty Optional
return status.orElseThrow(IllegalStateException::new);
}
//Prefer
CODE RECIPE #7.1
Since Java 8
QUESTION #3
Optional<String> status = ... ;
if (status.isPresent()) {
System.out.println("Status: " + status.get());
}
CODE RECIPE #8
//Avoid
Optional<String> status = ... ;
status.ifPresent(System.out::println);
//Prefer
Since Java 8
CODE RECIPE #8.1
Optional<String> status = ... ;
if(status.isPresent()) {
System.out.println("Status: " + status.get());
} else {
System.out.println("Status not found");
}
CODE RECIPE #9
//Avoid
Optional<String> status = ... ;
status.ifPresentOrElse(
System.out::println,
() -> System.out.println("Status not found")
);
Since Java 9
CODE RECIPE #9.1
//Prefer
public Optional<String> fetchStatus() {
Optional<String> status = ... ;
Optional<String> defaultStatus = Optional.of("PENDING");
if (status.isPresent()) {
return status;
} else {
return defaultStatus;
}
}
CODE RECIPE #10
//Avoid
public Optional<String> getStatus() {
Optional<String> status = ... ;
return status.orElseGet(() -> Optional.<String>of("PENDING"));
}
//Avoid
CODE RECIPE #10.1
public Optional<String> getStatus() {
Optional<String> status = ... ;
Optional<String> defaultStatus = Optional.of("PENDING");
return status.or(() -> defaultStatus);
// or, without defining "defaultStatus"
return status.or(() -> Optional.of("PENDING"));
}
Since Java 9
CODE RECIPE #10.2
//Prefer
List<Product> products = ... ;
Optional<Product> product = products.stream()
.filter(p -> p.getPrice() < price)
.findFirst();
if (product.isPresent()) {
return product.get().getName();
} else {
return "NOT FOUND";
}
CODE RECIPE #11
//Avoid
Example 1
List<Product> products = ... ;
Optional<Product> product = products.stream()
.filter(p -> p.getPrice() < price)
.findFirst();
return product.map(Product::getName)
.orElse("NOT FOUND")
//Avoid
CODE RECIPE #11.1
List<Product> products = ... ;
return products.stream()
.filter(p -> p.getPrice() < price)
.findFirst()
.map(Product::getName)
.orElse("NOT FOUND");
CODE RECIPE #11.3
//Prefer
Optional<Cart> cart = ... ;
Product product = ... ;
if(!cart.isPresent() ||
!cart.get().getItems().contains(product)) {
throw new NoSuchElementException();
}
//Avoid
CODE RECIPE #11.4
Example 2
Optional<Cart> cart = ... ;
Product product = ... ;
...
cart.filter(c -> c.getItems().contains(product)).orElseThrow();
CODE RECIPE #11.5
//Prefer
public String getStatus() {
String status = ... ;
return Optional.ofNullable(status).orElse("PENDING");
}
CODE RECIPE #12
//Avoid
public String getStatus() {
String status = ... ;
return status == null ? "PENDING" : status;
}
CODE RECIPE #12.1
//Prefer
QUESTION #4
public class Employee {
private Optional<String> zip;
private Optional<String> zip = Optional.empty();
}
CODE RECIPE #13
//Avoid
public class Employee {
private String zip;
private String zip = "";
}
CODE RECIPE #13.1
//Prefer
public class Employee {
private final String name; // can not be null
private final Optional<String> postcode; // optional field, thus may be null
public Employee(String name, Optional<String> postcode) {
this.name = Objects.requireNonNull(name, () -> "Name cannot be null");
this.postcode = postcode;
}
public Optional<String> getPostcode() {
return postcode;
}
}
CODE RECIPE #14
//Avoid
public class Employee {
private final String name; // cannot be null
private final String postcode; // optional field, thus may be null
public Employee(String name, String postcode) {
this.name = Objects.requireNonNull(name, () -> "Name cannot be null");
this.postcode = postcode;
}
public Optional<String> getPostcode() {
return Optional.ofNullable(postcode);
}
}
CODE RECIPE #14.1
//Prefer
@Entity
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
@Column(name="employee_zip")
private Optional<String> postcode; // optional field, thus may be null
public Optional<String> getPostcode() {
return postcode;
}
public void setPostcode(Optional<String> postcode) {
this.postcode = postcode;
}
}
CODE RECIPE #15
//Avoid
@Entity
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
@Column(name="employee_zip")
private String postcode; // optional field, thus may be null
public Optional<String> getPostcode() {
return Optional.ofNullable(postcode);
}
public void setPostcode(String postcode) {
this.postcode = postcode;
}
}
CODE RECIPE #15.1
//Prefer
public void renderCustomer(Cart cart, Optional<Renderer> renderer,
Optional<String> name) {
if (cart == null) {
throw new IllegalArgumentException("Cart cannot be null");
}
Renderer customerRenderer = renderer.orElseThrow(
() -> new IllegalArgumentException("Renderer cannot be null")
);
String customerName = name.orElseGet(() -> "anonymous");
}
// call the method - don't do this
renderCustomer(cart, Optional.<Renderer>of(CoolRenderer::new), Optional.empty());
RECIPE CODE #16
//Avoid
public void renderCustomer(Cart cart, Renderer renderer, String name) {
if (cart == null) {
throw new IllegalArgumentException("Cart cannot be null");
}
if (renderer == null) {
throw new IllegalArgumentException("Renderer cannot be null");
}
String customerName = Objects.requireNonNullElseGet(name, () -> "anonymous");
...
}
// call this method
renderCustomer(cart, new CoolRenderer(), null);
CODE RECIPE #16.1
//Prefer
public void renderCustomer(Cart cart, Renderer renderer, String name) {
Objects.requireNonNull(cart, "Cart cannot be null");
Objects.requireNonNull(renderer, "Renderer cannot be null");
String customerName = Objects.requireNonNullElseGet(name, () -> "anonymous");
...
}
// call this method
renderCustomer(cart, new CoolRenderer(), null);
CODE RECIPE #16.2
//Prefer
public void renderCustomer(Cart cart, Renderer renderer, String name) {
MyObjects.requireNotNullOrElseThrow(cart,
() -> new IllegalArgumentException("Cart cannot be null"));
MyObjects.requireNotNullOrElseThrow(renderer,
() -> new IllegalArgumentException("Renderer cannot be null"));
String customerName = Objects.requireNonNullElseGet(name, () -> "anonymous");
...
}
// call this method
renderCustomer(cart, new CoolRenderer(), null);
CODE RECIPE #16.3
//Prefer
// write your own helper
public final class MyObjects {
private MyObjects() {
throw new AssertionError("Cannot create instances for you!");
}
public static <T, X extends Throwable> T requireNotNullOrElseThrow(T obj,
Supplier<? extends X> exceptionSupplier) throws X {
if (obj != null) {
return obj;
} else {
throw exceptionSupplier.get();
}
}
}
public Optional<List<String>> getCartItems(long id) {
Cart cart = ... ;
List<String> items = cart.getItems(); // this may return null
return Optional.ofNullable(items);
}
RECIPE CODE #17
//Avoid
public List<String> getCartItems(long id) {
Cart cart = ... ;
List<String> items = cart.getItems(); // this may return null
return items == null ? Collections.emptyList() : items;
}
CODE RECIPE #17.1
//Prefer
Map<String, Optional<String>> items = new HashMap<>();
items.put("X1", Optional.ofNullable(...));
items.put("X2", Optional.ofNullable(...));
Optional<String> item = items.get("X1");
if (item == null) {
System.out.println("This key cannot be found");
} else {
String unwrappedItem = item.orElse("NOT FOUND");
System.out.println("Key found, Item: " + unwrappedItem);
}
RECIPE CODE #18
//Avoid
Map<String, String> items = new HashMap<>();
items.put("I1", "Shoes");
items.put("I2", null);
...
// get an item
String item = get(items, "I1"); // Shoes
String item = get(items, "I2"); // null
String item = get(items, "I3"); // NOT FOUND
private static String get(Map<String, String> map, String key) {
return map.getOrDefault(key, "NOT FOUND");
}
CODE RECIPE #18.1
//Prefer
CODE RECIPE #18.2
//Prefer
Map<Optional<String>, String> items = new HashMap<>();
Map<Optional<String>, Optional<String>> items = new HashMap<>();
//Avoid, this is even worse
RECIPE CODE #19
Since Java 8
public Optional<String> fetchItemName(long id) {
String itemName = ... ; // this may result in null
return Optional.ofNullable(itemName); // no risk for NPE
}
//Prefer
public Optional<String> fetchItemName(long id) {
String itemName = ... ; // this may result in null
return Optional.of(itemName); // this throws NPE if "itemName" is null :(
}
//Avoid
// no risk to NPE
return Optional.of("PENDING");
//Prefr
// ofNullable doesn't add any value
return Optional.ofNullable("PENDING");
//Avoid
CODE RECIPE #19.1
Optional<Integer> price = Optional.of(50);
Optional<Long> price = Optional.of(50L);
Optional<Double> price = Optional.of(50.43d);
RECIPE CODE #20
//Avoid
// unwrap via getAsInt()
OptionalInt price = OptionalInt.of(50);
// unwrap via getAsLong()
OptionalLong price = OptionalLong.of(50L);
// unwrap via getAsDouble()
OptionalDouble price = OptionalDouble.of(50.43d);
CODE RECIPE #20.1
//Prefer
Since Java 8
QUESTION #5
RECIPE CODE #21
Optional<String> actualItem = Optional.of("Shoes");
Optional<String> expectedItem = Optional.of("Shoes");
assertEquals(expectedItem, actualItem);
//Prefer
Optional<String> actualItem = Optional.of("Shoes");
Optional<String> expectedItem = Optional.of("Shoes");
assertEquals(expectedItem.get(), actualItem.get());
//Avoid
RECIPE CODE #22
Product product = new Product();
Optional<Product> op1 = Optional.of(product);
Optional<Product> op2 = Optional.of(product);
// op1.equals(op2) => true,expected true
if (op1.equals(op2))
//Prefer
Product product = new Product();
Optional<Product> op1 = Optional.of(product);
Optional<Product> op2 = Optional.of(product);
// op1 == op2 => false, expected true
if (op1 == op2)
//Avoid
public boolean isValidPasswordLength(User userId) {
Optional<String> password = ...; // User password
if (password.isPresent()) {
return password.get().length() > 5;
}
return false;
}
RECIPE CODE #23
//Avoid
public boolean isValidPasswordLength(User userId) {
Optional<String> password = ...; // User password
return password.filter((p) -> p.length() > 5).isPresent();
}
CODE RECIPE #23.1
//Prefer
Since Java 8
//Java 8
public Optional<String> getCartItems(long id) {
Cart cart = ... ; // this may be null
...
return Optional.ofNullable(cart);
}
public boolean isEmptyCart(long id) {
Optional<String> cart = getCartItems(id);
return !cart.isPresent();
}
RECIPE CODE #24
//Avoid
public Optional<String> getCartItems(long id) {
Cart cart = ... ; // this may be null
return Optional.ofNullable(cart);
}
public boolean isEmptyCart(long id) {
Optional<String> cart = getCartItems(id);
return cart.isEmpty();
}
Since Java 11
//Prefer
CODE RECIPE #24.1
Optional<String> lowername ...; // may be empty
// transform name to upper case
Optional<String> uppername;
if (lowername.isPresent()) {
uppername = Optional.of(lowername.get().toUpperCase());
} else {
uppername = Optional.empty();
}
RECIPE CODE #25
//Avoid
//Prefer
CODE RECIPE #25.1
Optional<String> lowername ...; // may be empty
// transform name to upper case
Optional<String> uppername = lowername.map(String::toUpperCase);
Example 1
// Avoid
List<Product> products = ... ;
Optional<Product> product = products.stream()
.filter(p -> p.getPrice() < 50)
.findFirst();
String name;
if (product.isPresent()) {
name = product.get().getName().toUpperCase();
} else {
name = "NOT FOUND";
}
// getName() return a non-null String
public String getName() {
return name;
}
CODE RECIPE #25.2
Example 2
//Prefer
List<Product> products = ... ;
String name = products.stream()
.filter(p -> p.getPrice() < 50)
.findFirst()
.map(Product::getName)
.map(String::toUpperCase)
.orElse("NOT FOUND");
// getName() return a String
public String getName() {
return name;
}
Since Java 8
// AVOID
List<Product> products = ... ;
Optional<Product> product = products.stream()
.filter(p -> p.getPrice() < 50)
.findFirst();
String name = null;
if (product.isPresent()) {
name = product.get().getName().orElse("NOT FOUND").toUpperCase();
}
// getName() return an Optional
public Optional<String> getName() {
return Optional.ofNullable(name);
}
Since Java 8
//Prefer
CODE RECIPE #25.3
List<Product> products = ... ;
String name = products.stream()
.filter(p -> p.getPrice() < 50)
.findFirst()
.flatMap(Product::getName)
.map(String::toUpperCase)
.orElse("NOT FOUND");
// getName() return an Optional
public Optional<String> getName() {
return Optional.ofNullable(name);
}
RECIPE CODE #26
public List<Product> getProductList(List<String> productId) {
return productId.stream()
.map(this::fetchProductById)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(toList());
}
public Optional<Product> fetchProductById(String id) {
return Optional.ofNullable(...);
}
//Avoid
CODE RECIPE #26.1
Since Java 9
//Prefer
* Also, we can convert Optional to List
public static <T> List<T> convertOptionalToList(Optional<T> optional) {
return optional.stream().collect(toList());
}
public List<Product> getProductList(List<String> productId) {
return productId.stream()
.map(this::fetchProductById)
.flatMap(Optional::stream)
.collect(toList());
}
public Optional<Product> fetchProductById(String id) {
return Optional.ofNullable(...);
}
By Mohamed Taman
Do you use the Optional class as it should be? This presentation will show how to use Optional class effectively, with a list of best practices with around 25+ recipes to use Optional class Effectively which Is Not Optional.
Chief Solutions Architect, Owner/CEO of SiriusXI, a Java Champion, Oracle ACE, JCP member, Consultant, Speaker, and Author.