如何正確使用Java8的Optional機制
Java8帶來的函數(shù)式編程特性對于習慣命令式編程的程序員來說還是有一定的障礙的,我們只有深入了解這些機制的方方面面才能運用自如。Null的處理在JAVA編程中是出了try catch之外的另一個頭疼的問題,需要大量的非空判斷模板代碼,程序邏輯嵌套層次太深。尤其是對集合的使用,需要層層判空。
首先來看下Optional類的結構圖:
1,Optional擁有兩個字段
- /**
- * Common instance for {@code empty()}.
- */
- private static final Optional<?> EMPTY = new Optional<>();
- /**
- * If non-null, the value; if null, indicates no value is present
- */
- private final T value;
1)EMPTY持有某個類型的空值結構,調用empty()返回的即是該實例
- public static<T> Optional<T> empty() {
- @SuppressWarnings("unchecked")
- Optional<T> t = (Optional<T>) EMPTY;
- return t;
- }
2)T vaule是該結構的持有的值
2,Optional的方法
1)構造函數(shù)
- private Optional() {
- this.value = null;
- }
- private Optional(T value) {
- this.value = Objects.requireNonNull(value);
- }
Optional(T value)如果vaule為null就會拋出NullPointer異常,所以對于使用的場景這兩個構造器都適用.
2)生成Optional對象
有兩個方法 of(T)和ofNullable(T)
- public static <T> Optional<T> of(T value) {
- return new Optional<>(value);
- }
- public static <T> Optional<T> ofNullable(T value) {
- return value == null ? empty() : of(value);
- }
of是直接調用的構造函數(shù),因此如果T為null則會拋出空指針異常
ofNullable對null進行了處理,會返回EMPTY的實例,因此不會出現(xiàn)異常
所以只有對于明確不會為null的對象才能直接使用of
3)獲取Optional對象的值
需要擯棄的使用方式
if(value.isPresent){
....
}else{
T t = value.get();
}
這種使用方式無異于傳統(tǒng)的if(vaule != null)
正確的使用姿勢:
orElse:如果值為空則返回指定的值
orElseGet:如果值為空則調用指定的方法返回
orElseThrow:如果值為空則直接拋出異常
- public T orElse(T other) {
- return value != null ? value : other;
- }
- public T orElseGet(Supplier<? extends T> other) {
- return value != null ? value : other.get();
- }
- public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
- if (value != null) {
- return value;
- } else {
- throw exceptionSupplier.get();
- }
- }
一般我們使用orElse來取值,如果不存在返回默認值.
4)Optional的中間處理
filter,map,flatMap,這幾個操作跟Stream的處理類似,只是要注意flatMap處理必須手動指定返回類型為Optional<U>,而map會自動將返回值包裝成Optional.舉個栗子,我們有商品很訂單的結構:
- package model;
- import java.util.List;
- /**
- * @auth gongxufan
- * @Date 2017/10/23
- **/
- public class Goods {
- private String goodsName;
- private double price;
- private List<Order> orderList;
- public String getGoodsName() {
- return goodsName;
- }
- public void setGoodsName(String goodsName) {
- this.goodsName = goodsName;
- }
- public double getPrice() {
- return price;
- }
- public void setPrice(double price) {
- this.price = price;
- }
- public List<Order> getOrderList() {
- return orderList;
- }
- public void setOrderList(List<Order> orderList) {
- this.orderList = orderList;
- }
- }
- package model;
- import java.time.LocalDateTime;
- /**
- * @auth gongxufan
- * @Date 2017/10/23
- **/
- public class Order {
- private LocalDateTime createTime;
- private LocalDateTime finishTime;
- private String orderName;
- private String orderUser;
- public LocalDateTime getCreateTime() {
- return createTime;
- }
- public void setCreateTime(LocalDateTime createTime) {
- this.createTime = createTime;
- }
- public LocalDateTime getFinishTime() {
- return finishTime;
- }
- public void setFinishTime(LocalDateTime finishTime) {
- this.finishTime = finishTime;
- }
- public String getOrderName() {
- return orderName;
- }
- public void setOrderName(String orderName) {
- this.orderName = orderName;
- }
- public String getOrderUser() {
- return orderUser;
- }
- public void setOrderUser(String orderUser) {
- this.orderUser = orderUser;
- }
- }
現(xiàn)在我有一個goodsOptional
- Optional<Goods> goodsOptional = Optional.ofNullable(new Goods());
現(xiàn)在我需要獲取goodsOptional里邊的orderList,應該這樣你操作
- goodsOptional.flatMap(g ->Optional.ofNullable(g.getOrderList())).orElse(Collections.emptyList())
flatMap里頭返回的是Optional<List<Order>>,然后我們再使用orElse進行unwraap.因此faltMap可以解引用更深層次的的對象鏈.
5)檢測Optional并執(zhí)行動作
- public void ifPresent(Consumer<? super T> consumer) {
- if (value != null)
- consumer.accept(value);
- }
這是一個終端操作,不像上邊的可以進行鏈式操作.在Optional實例使用直接調用,如果value存在則會調用指定的消費方法.舉個栗子:
- Goods goods = new Goods();
- Optional<Goods> goodsOptional = Optional.ofNullable(goods);
- List<Order> orderList = new ArrayList<>();
- goods.setOrderList(orderList);
- goodsOptional.flatMap(g ->Optional.ofNullable(g.getOrderList())).ifPresent((v)-> System.out.println(v));
至此該類的方法和使用介紹都差不多了,最后總結需要注意的地方:
1)Optional應該只用處理返回值,而不應該作為類的字段或者方法的參數(shù).因為這樣會造成額外的復雜度.
2)使用Option應該避免直接適應構造器和get,而應該使用isElse的系列方法避免頻繁的非空判斷
3)map和flatMap要注意區(qū)分使用場景