Optional设计者布赖恩·戈茨说过Optional旨在为库方法返回类型提供一种有限的机制,其中需要一种明确的方式来表示"无结果",而对这种情况使用null极有可能导致错误。
那么,如何以预期的方式使用Optional呢?,通常,我们通过学习如何不使用它到学习使用它们过程,通过26的示例来优雅而轻松解决Optional的使用情况。
1、不要赋值Optional以null值
-
避免
-
public Optional<Cart> fetchCart() { Optional<Cart> emptyCart = null; ... }
-
-
改进 (使用Optional.empty())
-
public Optional<Cart> fetchCart() { Optional<Cart> emptyCart = Optional.empty(); ... }
-
-
用Optional.empty()来初始化一个Optional而不是一个null值,Optional 只是一个容器或者说盒子,用空值初始化它没有任何意义的。
2、在调用Optional.get()之前确保Optional具有值
-
避免
-
Optional<Cart> cart = ... ; // 这个很容易为空 ... // 如果 "cart"为空那么这个方法将会抛出 java.util.NoSuchElementException Cart myCart = cart.get();
-
-
改进(使用 isPresent()进行预先判断是否有值)
-
if (cart.isPresent()) { Cart myCart = cart.get(); ... //写"myCart"一些逻辑 } else { ... // 这时候就不能调用cart.get()方法 }
-
3、当没有值存在时,通过Optional.orElse()方法设置/返回一个已经构造好的默认对象
-
简化isPresent – get()这种方式,一行代码替换if else
-
避免
-
public static final String USER_STATUS = "UNKNOWN"; ... public String findUserStatus(long id) { Optional<String> status = ... ; // 很容易返回一个空的 Optional if (status.isPresent()) { return status.get(); } else { return USER_STATUS; } }
-
-
改进
-
public static final String USER_STATUS = "UNKNOWN"; ... public String findUserStatus(long id) { Optional<String> status = ... ; // 很容易返回一个空的 Optional return status.orElse(USER_STATUS); }
-
4、当没有值存在时,通过Optional.orElseGet()方法设置/返回一个已经构造好的默认对象
-
orElse 和orElseGet区别就是,值不为空也会执行orElse代码, orElseGet不会执行(减少一次调用)
-
避免1
-
public String computeStatus() { ... // 用于计算状态代码 } public String findUserStatus(long id) { Optional<String> status = ... ; // 很容易返回一个空的 Optional if (status.isPresent()) { return status.get(); } else { return computeStatus(); } }
-
-
避免2
-
public String computeStatus() { ... // 用于计算状态代码 } public String findUserStatus(long id) { Optional<String> status = ... ; // 很容易返回一个空的 Optional // 即使 "status" 不为空,computeStatus()也会被调用 return status.orElse(computeStatus()); }
-
-
改进
-
public String computeStatus() { ... // 用于计算状态代码}public String findUserStatus(long id) { Optional<String> status = ... ; // 很容易返回一个空的 Optional // 仅 "status" 为空,computeStatus()会被调用 return status.orElseGet(this::computeStatus);}
-
5、当没有值存在时,从Java10开始通过orElseThrow()抛出java.util.NoSuchElementException异常
-
如果java 8 和java9 可以考虑使用第6点
-
避免
-
public String findUserStatus(long id) { Optional<String> status = ... ; // 很容易返回一个空的 Optional if (status.isPresent()) { return status.get(); } else { throw new NoSuchElementException(); }}
-
-
改进
-
public String findUserStatus(long id) { Optional<String> status = ... ; // 很容易返回一个空的 Optional return status.orElseThrow();}
-
6、当没有值存在时,通过orElseThrow(Supplier<? extends X> exceptionsSupplier) 抛出一个显式定义异常
-
不像第5点默认就是NoSuchElementException异常,这里需要显式定义
-
避免
-
public String findUserStatus(long id) { Optional<String> status = ... ; // 很容易返回一个空的 Optional if (status.isPresent()) { return status.get(); } else { throw new IllegalStateException(); }}
-
-
改进
-
public String findUserStatus(long id) { Optional<String> status = ... ; // 很容易返回一个空的 Optional return status.orElseThrow(IllegalStateException::new);}
-
7、当你有一个可选项并且需要一个空引用时,可以使用orElse(null)
-
避免
-
Method myMethod = ... ;...// contains an instance of MyClass or empty if "myMethod" is staticOptional<MyClass> instanceMyClass = ... ;...if (instanceMyClass.isPresent()) { myMethod.invoke(instanceMyClass.get(), ...); } else { myMethod.invoke(null, ...); }
-
-
改进
-
Method myMethod = ... ;...// contains an instance of MyClass or empty if "myMethod" is staticOptional<MyClass> instanceMyClass = ... ;...myMethod.invoke(instanceMyClass.orElse(null), ...);
-
8、使用Optional如果它存在。如果它不存在,则什么都不做。这是Optional.ifPresent()满足要求
-
避免
-
Optional<String> status = ... ;...if (status.isPresent()) { System.out.println("Status: " + status.get());}
-
-
改进
-
Optional<String> status ... ;...status.ifPresent(System.out::println);
-
9、使用Optional如果它存在和不存在情况都进行对应逻辑,java9 Optional.ifPresentElse()可以胜任
-
避免
-
Optional<String> status = ... ;if(status.isPresent()) { System.out.println("Status: " + status.get());} else { System.out.println("Status not found");}
-
-
改进
-
Optional<String> status = ... ;status.ifPresentOrElse( System.out::println, () -> System.out.println("Status not found"));
-
10、在java9, 当值存在时,设置/返回该可选值,当没有值存在时,设置/返回其他可选。Optional.or()可以满足要求
-
避免1
-
public Optional<String> fetchStatus() { Optional<String> status = ... ; Optional<String> defaultStatus = Optional.of("PENDING"); if (status.isPresent()) { return status; } else { return defaultStatus; } }
-
-
避免2
-
public Optional<String> fetchStatus() { Optional<String> status = ... ; return status.orElseGet(() -> Optional.<String>of("PENDING"));}
-
-
改进
-
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是Lambdas中isPresent()-get()对的完美替代品
-
例子1
-
避免1
-
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";}
-
-
避免2
-
List<Product> products = ... ;Optional<Product> product = products.stream() .filter(p -> p.getPrice() < price) .findFirst();return product.map(Product::getName) .orElse("NOT FOUND");
-
-
改进
-
List<Product> products = ... ;return products.stream() .filter(p -> p.getPrice() < price) .findFirst() .map(Product::getName) .orElse("NOT FOUND");
-
-
-
例子2
-
避免
-
Optional<Cart> cart = ... ;Product product = ... ;...if(!cart.isPresent() || !cart.get().getItems().contains(product)) { throw new NoSuchElementException();}
-
-
改进
-
Optional<Cart> cart = ... ;Product product = ... ;...cart.filter(c -> c.getItems().contains(product)).orElseThrow();
-
-
12、避免以获取值单一目的而使用Optional的方法(滥用)
-
避免
-
public String fetchStatus() { String status = ... ; return Optional.ofNullable(status).orElse("PENDING");}
-
-
改进
-
public String fetchStatus() { String status = ... ; return status == null ? "PENDING" : status;}
-
13、不要声明任何类型的字段是Optional
-
它没有实现Serializable接口,它定义目的也不是作为java Bean的字段
-
避免
-
public class Customer { [access_modifier] [static] [final] Optional<String> zip; [access_modifier] [static] [final] Optional<String> zip = Optional.empty(); ...}
-
-
改进
-
public class Customer { [access_modifier] [static] [final] String zip; [access_modifier] [static] [final] String zip = ""; ...}
-
14、不要在构造函数中使用Optional(自己可能为空)
-
避免
-
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; } ...}
-
-
改进
-
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、不要在Setters参数使用Optional
-
避免
-
@Entitypublic 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@Entitypublic 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、不用在方法参数中使用Optional参数
-
避免1
-
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 thisrenderCustomer(cart, Optional.<Renderer>of(CoolRenderer::new), Optional.empty());
-
-
改进
-
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 methodrenderCustomer(cart, new CoolRenderer(), null);
-
-
改进1(判空异常)
-
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 methodrenderCustomer(cart, new CoolRenderer(), null);
-
-
改进2 ( 不想要默认抛出空指针异常,自定义异常工具类)
-
// 自己写工具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 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 methodrenderCustomer(cart, new CoolRenderer(), null);
-
17、不要使用Optional返回空集合或数组
-
对于集合和数组,我们要记住可以世俗Collections.emptyList(), emptyMap() 和emptySet()
-
避免
-
public Optional<List<String>> fetchCartItems(long id) { Cart cart = ... ; List<String> items = cart.getItems(); // this may return null return Optional.ofNullable(items);}
-
-
改进
-
public List<String> fetchCartItems(long id) { Cart cart = ... ; List<String> items = cart.getItems(); // this may return null return items == null ? Collections.emptyList() : items;}
-
18、避免在集合使用使用Optional
-
避免
-
// AVOIDMap<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);}
-
-
改进
-
Map<String, String> items = new HashMap<>();items.put("I1", "Shoes");items.put("I2", null);...// get an itemString item = get(items, "I1"); // ShoesString item = get(items, "I2"); // nullString item = get(items, "I3"); // NOT FOUNDprivate static String get(Map<String, String> map, String key) { return map.getOrDefault(key, "NOT FOUND");}
-
19、不用混淆Optional.of()和Optional.ofNullable()方法
-
Optional.of(null)将会抛出NullPointerException, Optional.ofNullable(null) 结果将会返回Optional.empty
-
使用Optional.of替换Optional.ofNullable例子
-
避免
-
public Optional<String> fetchItemName(long id) { String itemName = ... ; // this may result in null ... return Optional.of(itemName); // this throws NPE if "itemName" is null :(}
-
-
改进
-
public Optional<String> fetchItemName(long id) { String itemName = ... ; // this may result in null ... return Optional.ofNullable(itemName); // no risk for NPE }
-
-
-
使用Optional.ofNullable替换Optional.of例子
-
避免
-
return Optional.ofNullable("PENDING");
-
-
改进
-
return Optional.of("PENDING"); // no risk to NPE
-
-
20、尽量选择非泛型OptionalInt、OptionalLong或OptionalDouble,而不是Optional
-
减少拆箱和装箱性能损耗
-
避免
-
Optional<Integer> price = Optional.of(50);Optional<Long> price = Optional.of(50L);Optional<Double> price = Optional.of(50.43d);
-
-
改进
-
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、并不需要去掉Optionals去断言是否相等
-
避免
-
Optional<String> actualItem = Optional.of("Shoes");Optional<String> expectedItem = Optional.of("Shoes"); assertEquals(expectedItem.get(), actualItem.get());
-
-
改进
-
Optional<String> actualItem = Optional.of("Shoes");Optional<String> expectedItem = Optional.of("Shoes"); assertEquals(expectedItem, actualItem);
-
22、通过Map()和flatMap()转换值
-
例子(使用map)
-
避免
-
Optional<String> lowername ...; // may be empty// transform name to upper caseOptional<String> uppername;if (lowername.isPresent()) { uppername = Optional.of(lowername.get().toUpperCase());} else { uppername = Optional.empty();}
-
-
改进
-
Optional<String> lowername ...; // may be empty// transform name to upper caseOptional<String> uppername = lowername.map(String::toUpperCase);
-
-
-
例子2(连续使用)
-
避免
-
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 Stringpublic String getName() { return name;}
-
-
改进
-
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 Stringpublic String getName() { return name;}
-
-
-
使用flatMap()
-
避免
-
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 Optionalpublic Optional<String> getName() { return Optional.ofNullable(name);}
-
-
改进
-
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 Optionalpublic Optional<String> getName() { return Optional.ofNullable(name);}
-
-
23、使用filter()拒绝基于预定义规则的包装值
-
避免
-
public boolean validatePasswordLength(User userId) { Optional<String> password = ...; // User password if (password.isPresent()) { return password.get().length() > 5; } return false;}
-
-
改进
-
public boolean validatePasswordLength(User userId) { Optional<String> password = ...; // User password return password.filter((p) -> p.length() > 5).isPresent();}
-
24、我们是否需要将Optional API与Stream API 连接起来
-
避免
-
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(...);}
-
-
改进
-
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、避免在Optional上使用敏感操作
-
== 或 同步方法
-
避免
-
Product product = new Product();Optional<Product> op1 = Optional.of(product);Optional<Product> op2 = Optional.of(product);// op1 == op2 => false, expected trueif (op1 == op2) { ...
-
-
改进
-
Product product = new Product();Optional<Product> op1 = Optional.of(product);Optional<Product> op2 = Optional.of(product);// op1.equals(op2) => true,expected trueif (op1.equals(op2)) { ...
-
-
永远不要这么做
-
Optional<Product> product = Optional.of(new Product());synchronized(product) { ...}
-
26、Java11增加了Optional.isEmpty()方法,返回一个boolean值
-
避免
-
// 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 (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();}
-
-
码完了。。。
总结
- 尽量保证Optional本身不能为null
- 能不用Optional就不用Optional
- 尽量不使用 isPresent-get()模式,寻找替代orElseXXX方法
- 不用使用Optional作为同步锁对象 ,具体原因Value-based classes should not be used for locking
结尾
- 感谢大家的耐心阅读,如有建议请私信或评论留言。
- 如有收获,劳烦支持,关注、点赞、评论、收藏均可,博主会经常更新,与大家共同进步
- 下一篇研究一下创建SocketFrameHandler类