一、傳統寫法
@Data
public class SkuVO {
private Long skuId;
private Price price;
}
@Data
public class Price {
private BigDecimal mallPrice;
private BigDecimal sellPrice;
}
有一個SKU對象,里面包含一個skuId和一個price對象,price對象里面有市場價和成本價。假如現在有個需求,獲取sku里面的mallPrice,並且返回。
毫無疑問,NPE相信每個程序員都不可能沒遇到過。jdk1.8以前一般是這么寫:
private static BigDecimal fetchMallprice(SkuVO skuVO) throws Exception {
if (skuVO != null) {
Price price = skuVO.getPrice();
if (price != null) {
BigDecimal mallPrice = price.getMallPrice();
if (mallPrice != null) {
if (mallPrice.compareTo(new BigDecimal("10")) == 1) {
return mallPrice;
}
}
}
}
throw new Exception("skuVO不符合要求,請檢查");
}
其實在真實項目中,這種寫法實在是太普遍了,各種非空判斷才敢往下執行,否則就會拋出NPE。但是這種寫法if嵌套得太多了,可讀性很差。
所以我們也可以像這樣,提前判斷是否為空,為空則拋異常:
private static BigDecimal fetchMallprice(SkuVO skuVO) throws Exception {
if (skuVO == null) {
throw new Exception("skuVO不符合要求,請檢查");
}
Price price = skuVO.getPrice();
if (price == null) {
throw new Exception("skuVO不符合要求,請檢查");
}
BigDecimal mallPrice = price.getMallPrice();
if (mallPrice != null) {
if (mallPrice.compareTo(new BigDecimal("10")) == 1) {
return mallPrice;
}
}
throw new Exception("skuVO不符合要求,請檢查");
}
雖然嵌套減少了,但是還是比較臃腫的。JDK1.8出來后,我們就可以優雅地寫這種功能了。
二、JDK1.8寫法
private static BigDecimal fetchMallprice(SkuVO skuVO) throws Exception {
return Optional.ofNullable(skuVO)
.map(s -> s.getPrice())
.map(p -> p.getMallPrice())
.filter(m -> m.compareTo(new BigDecimal("10")) == 1)
.orElseThrow(() -> new Exception("skuVO不合法"));
}
Optional是jdk1.8出的新特性,其實思想挺簡單的,就是把實體包裝了一層,包裝的時候,如果是實體為空,則返回一個空的Optional,否則返回Optional
Optional.ofNullable(skuVO)首先構造一個Optional,map(Function<? super T, ? extends U> mapper)接收一個Funtion,filter過濾map返回的值,最后通過orElseThrow拋出異常。
三、源碼分析
3.1 構造方法
private Optional(T value) {
this.value = Objects.requireNonNull(value);
}
private Optional() {
this.value = null;
}
Optional的構造方法都是private,所以其提供了三個靜態public方法來構造Optional:
private static final Optional<?> EMPTY = new Optional<>();
private final T value;
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return 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);
}
EMPTY是當Optional為空的時候返回的。
T是實際值。
empty()方法返回一個空實例EMPTY,即Optional里面不包含值,那么我們使得 Optional 只存在 包含值 和 不包含值 兩種狀態。
of(T value)方法將新建一個非空的Optional,如果value為空,那么會拋出NPE。
ofNullable(T value)方法里的value可以為空,如果為空,則調用empty()方法空的Optional,否則調用of(T value)方法一個非空的Optional。
3.2 ifPresent
public void ifPresent(Consumer<? super T> consumer) {
if (value != null) {
consumer.accept(value);
}
}
如果當前Optional不為空,則調用consumer.accept(value)方法,而Consumer又是函數式接口,故利用lambda表達式可以這樣寫:
Optional<SkuVo> sku = Optional.ofNullable(getSku());
sku.ifPresent(s -> System.out.println(s.getSkuId()));
3.3 orElse
public T orElse(T other) {
return value != null ? value : other;
}
如果Optional不為空,則返回,否則執行orElse傳入的參數。
3.4 orElseGet
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}
如果Optional不為空,則返回,否則返回Supplier實現類的值,Supplier也是函數式接口,里面只有get()方法。
3.5 orElseThrow
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
同orElseGet,只是但當Optional為空的時候,會拋出異常,拋出的異常由傳入的異常exceptionSupplier提供。
3.6 map
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Optional.ofNullable(mapper.apply(value));
}
}
當Optional為空的時候,返回empty(),否則返回一個新的Optional,該Optional包裝的是mapper以value作為輸入的輸出值。
3.7 flatMap
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Objects.requireNonNull(mapper.apply(value));
}
}
flatMap和map方法的區別是,map方法參數中的mapper輸出的是值,map方法會使用Optional.ofNullable將其包裝成Optional,而flatMap要求參數中的mapper輸出的就是Optional。
3.8 filter
public Optional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}
filter方法接受一個Predicate來對Optional包含的值進行過濾,如果包含的值滿足條件,那么還是返回這個Optional,否則返回empty()。