如何正確使用Java8的Optional機制

gongxufan發表於2018-06-21

Java8帶來的函數語言程式設計特性對於習慣指令式程式設計的程式設計師來說還是有一定的障礙的,我們只有深入瞭解這些機制的方方面面才能運用自如。Null的處理在JAVA程式設計中是出了try catch之外的另一個頭疼的問題,需要大量的非空判斷模板程式碼,程式邏輯巢狀層次太深。尤其是對集合的使用,需要層層判空。

首先來看下Optional類的結構圖:
img

屬性

/** 
 * 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是該結構的持有的值

方法

建構函式

private Optional() {  
        this.value = null;  
    }  
private Optional(T value) {  
        this.value = Objects.requireNonNull(value);  
    }複製程式碼

Optional(T value)如果vaule為null就會丟擲NullPointer異常,所以對於使用的場景這兩個構造器都適用.

生成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是直接呼叫的建構函式,因此如果T為null則會丟擲空指標異常,ofNullable對null進行了處理,會返回EMPTY的例項,因此不會出現異常。

所以只有對於明確不會為null的物件才能直接使用of

獲取Optional物件的值

需要擯棄的使用方式

if(value.isPresent){
….
}else{
T t = value.get();
}

這種使用方式無異於傳統的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來取值,如果不存在返回預設值.

Optional的中間處理

filter,map,flatMap,這幾個操作跟Stream的處理類似,只是要注意flatMap處理必須手動指定返回型別為Optional,而map會自動將返回值包裝成Optional.舉個例子,我們有商品很訂單的結構:

package model;  
  
import java.util.List;  
  
/** 
 * @auth gongxufan 
 * @Date 2016/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 2016/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;  
    }  
}複製程式碼

現在我有一個goodsOptional

Optional<Goods> goodsOptional = Optional.ofNullable(new Goods());複製程式碼

現在我需要獲取goodsOptional裡邊的orderList,應該這樣你操作

goodsOptional.flatMap(g ->Optional.ofNullable(g.getOrderList())).orElse(Collections.emptyList())複製程式碼

flatMap裡頭返回的是Optional>,然後我們再使用orElse進行unwraap.因此faltMap可以解引用更深層次的的物件鏈.

檢測Optional並執行動作

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));複製程式碼

end

至此該類的方法和使用介紹都差不多了,最後總結需要注意的地方:
1) Optional應該只用處理返回值,而不應該作為類的欄位或者方法的引數.因為這樣會造成額外的複雜度.
2) 使用Option應該避免直接使用構造器和get,而應該使用orElse的系列方法避免頻繁的非空判斷
3) map和flatMap要注意區分使用場景


相關文章