1. Never Assign Null to an Optional Variable
Avoid:
// AVOID
public Optional<Cart> fetchCart() {
Optional<Cart> emptyCart = null;
...
}
Prefer:
// PREFER
public Optional<Cart> fetchCart() {
Optional<Cart> emptyCart = Optional.empty();
...
}
2. Ensure That an Optional Has a Value Before Calling Optional.get()
Avoid:
// AVOID
Optional<Cart> cart = ... ; // this is prone to be empty
...
// if "cart"is empty then this code will throw a java.util.NoSuchElementException
Cart myCart = cart.get();
Prefer:
// PREFER
if (cart.isPresent()) {
Cart myCart = cart.get();
... // do something with "myCart"
} else {
... // do something that doesn't call cart.get()
}
3. When No Value Is Present, Set/Return an Already-Constructed Default Object Via the Optional.orElse() Method
use orElse() only when the parameter (the defaultobject) is already constructed.
Avoid:
// AVOID
public static final String USER_STATUS = "UNKNOWN";
...
public String findUserStatus(long id) {
Optional<String> status = ... ; // prone to return an empty Optional
if (status.isPresent()) {
return status.get();
} else {
return USER_STATUS;
}
}
Prefer:
// PREFER
public static final String USER_STATUS = "UNKNOWN";
...
public String findUserStatus(long id) {
Optional<String> status = ... ; // prone to return an empty Optional
return status.orElse(USER_STATUS);
}
4. When No Value Is Present, Set/Return a Non-Existent Default Object Via the Optional.orElseGet() Method
The important thing here is that the parameter of orElseGet() is a Java 8, Supplier. This means that the Supplier method passed as an argument is only executed
when an Optional value is not present.
when an Optional value is not present.
Avoid:
// AVOID
public String computeStatus() {
... // some code used to compute status
}
public String findUserStatus(long id) {
Optional<String> status = ... ; // prone to return an empty Optional
if (status.isPresent()) {
return status.get();
} else {
return computeStatus();
}
}
Also, Avoid:
// AVOID
public String computeStatus() {
... // some code used to compute status
}
public String findUserStatus(long id) {
Optional<String> status = ... ; // prone to return an empty Optional
// computeStatus() is called even if "status" is not empty
return status.orElse(computeStatus());
}
Prefer:
// PREFER
public String computeStatus() {
... // some code used to compute status
}
public String findUserStatus(long id) {
Optional<String> status = ... ; // prone to return an empty Optional
// computeStatus() is called only if "status" is empty
return status.orElseGet(this::computeStatus);
}
5. When No Value Is Present, Throw a java.util.NoSuchElementException Exception Via orElseThrow() Since Java 10
Avoid:
// AVOID
public String findUserStatus(long id) {
Optional<String> status = ... ; // prone to return an empty Optional
if (status.isPresent()) {
return status.get();
} else {
throw new NoSuchElementException();
}
}
Prefer:
// PREFER
public String findUserStatus(long id) {
Optional<String> status = ... ; // prone to return an empty Optional
return status.orElseThrow();
}
6. When No Value Is Present, Throw an Explicit Exception Via orElseThrow(Supplier<? extends X>
exceptionSupplier)
Avoid:
// AVOID
public String findUserStatus(long id) {
Optional<String> status = ... ; // prone to return an empty Optional
if (status.isPresent()) {
return status.get();
} else {
throw new IllegalStateException();
}
}
Prefer:
// PREFER
public String findUserStatus(long id) {
Optional<String> status = ... ; // prone to return an empty Optional
return status.orElseThrow(IllegalStateException::new);
}
7. When You Have an Optional and Need a Null Reference, Use orElse(null)
Avoid:
// AVOID
Method myMethod = ... ;
...
// contains an instance of MyClass or empty if "myMethod" is static
Optional<MyClass> instanceMyClass = ... ;
...
if (instanceMyClass.isPresent()) {
myMethod.invoke(instanceMyClass.get(), ...);
} else {
myMethod.invoke(null, ...);
}
Prefer:
// PREFER
Method myMethod = ... ;
...
// contains an instance of MyClass or empty if "myMethod" is static
Optional<MyClass> instanceMyClass = ... ;
...
myMethod.invoke(instanceMyClass.orElse(null), ...);
8. Consume an Optional if it Is Present. Do Nothing if it Is Not Present. This Is a Job For
Optional.ifPresent().
Avoid:
// AVOID
Optional<String> status = ... ;
...
if (status.isPresent()) {
System.out.println("Status: " + status.get());
}
Prefer:
// PREFER
Optional<String> status ... ;
...
status.ifPresent(System.out::println);
9. Consume an Optional if it Is Present. If it Is Not Present, Then Execute an Empty-Based Action. This Is a Job
For Optional.ifPresentElse(), Java 9.
Avoid:
// AVOID
Optional<String> status = ... ;
if(status.isPresent()) {
System.out.println("Status: " + status.get());
} else {
System.out.println("Status not found");
}
Prefer:
// PREFER
Optional<String> status = ... ;
status.ifPresentOrElse(
System.out::println,
() -> System.out.println("Status not found")
);
10. When the Value Is Present, Set/Return That Optional. When No Value Is Present, Set/Return the Other
Optional. This Is a Job For Optional.or(), Java 9.
Avoid:
// AVOID
public Optional<String> fetchStatus() {
Optional<String> status = ... ;
Optional<String> defaultStatus = Optional.of("PENDING");
if (status.isPresent()) {
return status;
} else {
return defaultStatus;
}
}
Also, Avoid:
// AVOID
public Optional<String> fetchStatus() {
Optional<String> status = ... ;
return status.orElseGet(() -> Optional.<String>of("PENDING"));
}
Prefer:
// PREFER
public Optional<String> fetchStatus() {
Optional<String> status = ... ;
Optional<String> defaultStatus = Optional.of("PENDING");
return status.or(() -> defaultStatus);
// or, without defining "defaultStatus"
return status.or(() -> Optional.of("PENDING"));
}
11. Optional.orElse/ orElseXXX Are a Perfect Replacement for isPresent()-get() Pair in Lambdas
Sample 1
Avoid:
// AVOID
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";
}
Also, Avoid:
// AVOID
List<Product> products = ... ;
Optional<Product> product = products.stream()
.filter(p -> p.getPrice() < price)
.findFirst();
return product.map(Product::getName)
.orElse("NOT FOUND");
Prefer:
// PREFER
List<Product> products = ... ;
return products.stream()
.filter(p -> p.getPrice() < price)
.findFirst()
.map(Product::getName)
.orElse("NOT FOUND");
Sample 2
Avoid:
// AVOID
Optional<Cart> cart = ... ;
Product product = ... ;
...
if(!cart.isPresent() ||
!cart.get().getItems().contains(product)) {
throw new NoSuchElementException();
}
Prefer:
// PREFER
Optional<Cart> cart = ... ;
Product product = ... ;
...
cart.filter(c -> c.getItems().contains(product)).orElseThrow();
12. Avoid Chaining Optional’s Methods With the Single Purpose of Getting a Value
Avoid:
// AVOID
public String fetchStatus() {
String status = ... ;
return Optional.ofNullable(status).orElse("PENDING");
}
Prefer:
// PREFER
public String fetchStatus() {
String status = ... ;
return status == null ? "PENDING" : status;
}
13. Do Not Declare Any Field of Type Optional
Avoid:
// AVOID
public class Customer {
[access_modifier] [static] [final] Optional<String> zip;
[access_modifier] [static] [final] Optional<String> zip = Optional.empty();
...
}
Prefer:
// PREFER
public class Customer {
[access_modifier] [static] [final] String zip;
[access_modifier] [static] [final] String zip = "";
...
}
14. Do Not Use Optional in Constructors Arguments
Avoid:
// AVOID
public class Customer {
private final String name; // cannot be null
private final Optional<String> postcode; // optional field, thus may be null
public Customer(String name, Optional<String> postcode) {
this.name = Objects.requireNonNull(name, () -> "Name cannot be null");
this.postcode = postcode;
}
public Optional<String> getPostcode() {
return postcode;
}
...
}
Prefer:
// PREFER
public class Customer {
private final String name; // cannot be null
private final String postcode; // optional field, thus may be null
public Cart(String name, String postcode) {
this.name = Objects.requireNonNull(name, () -> "Name cannot be null");
this.postcode = postcode;
}
public Optional<String> getPostcode() {
return Optional.ofNullable(postcode);
}
...
}
15. Do Not Use Optional in Setters Arguments
Avoid:
// AVOID
@Entity
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
...
@Column(name="customer_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;
}
...
}
Prefer:
// PREFER
@Entity
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
...
@Column(name="customer_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;
}
...
}
16. Do Not Use Optional in Methods Arguments
Avoid:
// AVOID
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());
Prefer:
// PREFER
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);
Also, Prefer:
// 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);
17. Do Not Use Optional to Return Empty Collections or Arrays
Avoid:
// AVOID
public Optional<List<String>> fetchCartItems(long id) {
Cart cart = ... ;
List<String> items = cart.getItems(); // this may return null
return Optional.ofNullable(items);
}
Prefer:
// PREFER
public List<String> fetchCartItems(long id) {
Cart cart = ... ;
List<String> items = cart.getItems(); // this may return null
return items == null ? Collections.emptyList() : items;
}
18. Avoid Using Optional in Collections
Avoid:
// AVOID
Map<String, Optional<String>> items = new HashMap<>();
items.put("I1", Optional.ofNullable(...));
items.put("I2", Optional.ofNullable(...));
...
Optional<String> item = items.get("I1");
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);
}
Prefer:
//PREFER
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");
}
19. Do Not Confuse Optional.of() and Optional.ofNullable()
Avoid:
// AVOID
public Optional<String> fetchItemName(long id) {
String itemName = ... ; // this may result in null
...
return Optional.of(itemName); // this throws NPE if "itemName" is null :(
}
Prefer:
// PREFER
public Optional<String> fetchItemName(long id) {
String itemName = ... ; // this may result in null
...
return Optional.ofNullable(itemName); // no risk for NPE
}
20. Avoid Optional and Choose Non-Generic OptionalInt, OptionalLong, or OptionalDouble
Avoid:
// AVOID
Optional<Integer> price = Optional.of(50);
Optional<Long> price = Optional.of(50L);
Optional<Double> price = Optional.of(50.43d);
Prefer:
// PREFER
OptionalInt price = OptionalInt.of(50); // unwrap via getAsInt()
OptionalLong price = OptionalLong.of(50L); // unwrap via getAsLong()
OptionalDouble price = OptionalDouble.of(50.43d); // unwrap via getAsDouble()
21. There Is No Need to Unwrap Optionals for Asserting Equality
Avoid:
// AVOID
Optional<String> actualItem = Optional.of("Shoes");
Optional<String> expectedItem = Optional.of("Shoes");
assertEquals(expectedItem.get(), actualItem.get());
Prefer:
// PREFER
Optional<String> actualItem = Optional.of("Shoes");
Optional<String> expectedItem = Optional.of("Shoes");
assertEquals(expectedItem, actualItem);
22. Transform Values Via Map() and flatMap()
Using map()
Example1
Avoid:
// AVOID
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();
}
Prefer:
// PREFER
Optional lowername ...; // may be empty
// transform name to upper case
Optional uppername = lowername.map(String::toUpperCase);
Example2
Avoid:
// 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;
}
Prefer:
// 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;
}
Using flatMap()
Avoid:
// 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);
}
Prefer:
// PREFER
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);
}
23. Reject Wrapped Values Based on a Predefined Rule Using filter()
Avoid:
// AVOID
public boolean validatePasswordLength(User userId) {
Optional<String> password = ...; // User password
if (password.isPresent()) {
return password.get().length() > 5;
}
return false;
}
Prefer:
// PREFER
public boolean validatePasswordLength(User userId) {
Optional<String> password = ...; // User password
return password.filter((p) -> p.length() > 5).isPresent();
}
24. Do We Need to Chain the Optional API With the Stream API?
Practically, Optional.stream() allows us to replace filter() and map() with flatMap().
Avoid:
// AVOID
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(...);
}
Prefer:
// PREFER
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(...);
}
25. Avoid Using Identity-Sensitive Operations on Optionals
Avoid:
// AVOID
Product product = new Product();
Optional<Product> op1 = Optional.of(product);
Optional<Product> op2 = Optional.of(product);
// op1 == op2 => false, expected true
if (op1 == op2) { ...
Prefer:
// PREFER
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)) { ...
26. Return a boolean If The Optional Is Empty. Prefer Java 11, Optional.isEmpty()
Avoid:
// AVOID (Java 11+)
public Optional<String> fetchCartItems(long id) {
Cart cart = ... ; // this may be null
...
return Optional.ofNullable(cart);
}
public boolean cartIsEmpty(long id) {
Optional<String> cart = fetchCartItems(id);
return !cart.isPresent();
}
Prefer:
// PREFER (Java 11+)
public Optional<String> fetchCartItems(long id) {
Cart cart = ... ; // this may be null
...
return Optional.ofNullable(cart);
}
public boolean cartIsEmpty(long id) {
Optional<String> cart = fetchCartItems(id);
return cart.isEmpty();
}
[출처]
https://dzone.com/articles/using-optional-correctly-is-not-optional
0 Comments:
댓글 쓰기