java程式碼之美(16) ---Java8 Optional
阿新 • • 發佈:2020-02-21
Java8 Optional
一句話介紹Optional類:使用JDK8的Optional類來防止NullPointerException(空指標異常)問題
。
一、前言
在我們開放過程中,碰到的異常中NullPointerException必然是排行第一的。所以在平時編碼中,我們會時時的判斷null。
public void saveCity(City city) { if (city != null) { String cityName = city.getCityName(); if (cityName != null) { String code = cityDao.findCodeByName(cityName); city.setCode(code); cityDao.save(city); } } }
雖然上面程式碼變得更加安全,但是過多巢狀 if 語句降低程式碼整體可讀性,提高複雜度。我們可以優化下程式碼
public void saveCity(City city) { if (city == null) { return; } String cityName = city.getCityName(); if (cityName == null) { return; } String code = cityDao.findCodeByName(cityName); city.setCode(code); cityDao.save(city); }
這樣還可以,但我們通過Optional變的更簡潔
public void saveCity(City city) { //就一行 city不為空返回 城市名稱 否則直接返回空 Optional<String> roleOpt = Optional.ofNullable(city).map(City::getCityName); //如果容器中 不為空 if (roleOpt.isPresent()) { String code = cityDao.findCodeByName(roleOpt.get()); city.setCode(code); cityDao.save(city); } }
這樣,我們僅需要對我們關心的做一次校驗,省卻了前面的一系列的檢驗操作。
二、Optional API
概念
Optiona本質是一個容器,容器中存在為null或者不包含非null值的容器物件。提供了一系列的方法供我們判斷該容器裡的物件是否存在。
1、JDK原始碼
/**
* final修飾代表不能被子類繼承
*/
public final class Optional<T> {
/**
* 建立一個空容器
*/
private static final java.util.Optional<?> EMPTY = new java.util.Optional<>();
/**
* 傳入的值
*/
private final T value;
/**
* 建構函式私有化 說明不能被外部new
*/
private Optional() {
this.value = null;
}
/**
* 私有化建構函式
*/
private Optional(T value) {
this.value = Objects.requireNonNull(value);
}
/**
* 獲取空容器
*/
public static <T> java.util.Optional<T> empty() {
@SuppressWarnings("unchecked")
java.util.Optional<T> t = (java.util.Optional<T>) EMPTY;
return t;
}
/**
* 傳入的物件不能為空 否則拋異常
*/
public static <T> java.util.Optional<T> of(T value) {
return new java.util.Optional<>(value);
}
/**
* 傳入的物件可以為空
*/
public static <T> java.util.Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
/**
* 獲取容器物件的方法 注意 如果用這個方法則代表容器中一定有物件,否則拋異常
*/
public T get() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
/**
* 判斷容器物件是否為空
*/
public boolean isPresent() {
return value != null;
}
/**
* 如果容器物件為空 則返回當前物件
*/
public T orElse(T other) {
return value != null ? value : other;
}
//==========有關下面這幾個JDK8自帶的函式式介面的作用,上一篇部落格有詳細說明,這裡就不多說了。
/**
* 傳入Consumer程式設計式介面引數
*/
public void ifPresent(Consumer<? super T> consumer) {
if (value != null)
consumer.accept(value);
}
/**
* 傳入Predicate程式設計式介面引數
*/
public java.util.Optional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}
/**
* 傳入Function程式設計式介面引數
*/
public <U> java.util.Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return java.util.Optional.ofNullable(mapper.apply(value));
}
}
/**
* 傳入Function程式設計式介面引數
*/
public <U> java.util.Optional<U> flatMap(Function<? super T, java.util.Optional<U>> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Objects.requireNonNull(mapper.apply(value));
}
}
/**
* 傳入Supplier程式設計式介面引數
*/
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}
/**
* 傳入Supplier程式設計式介面引數
*/
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
}
2、建立Optional物件
通過上面原始碼可以看出,Optional的建構函式都是私有化的,無法直接new物件。它這邊提供了3個靜態方法獲取物件。
1、建立一個一定是空的Optional容器
Optional<Car> optCar = Optional.empty();
2、建立一個一定是非空值Optional容器
(傳入的物件不可以為null,否則丟擲NullPointerException)
Optional<Car> optUser = Optional.of(user);
3、建立一個可能是空也可能不為空的Optional容器
(傳入的物件可以為null)
Optional<Car> optUser = Optional.ofNullable(user);
3、總結常用方法
1、isPresent() //有值則返回true
2、get(): //值存在時返回值,否則丟擲一個NoSuchElement異常(所以調這個,一般先判斷上面方法返回是否為true)
3、orElse(T other) //值存在時返回值,否則返回一個預設值
4、ifPresent(Consumer<T> block) //會在值存在的時候執行給定的程式碼塊
5、orElseThrow(Supplier<? extends X> exceptionSupplier) //與get()類似,不同的是可以自定義異常型別
6、orElseGet(Supplier<? extends T> other) //orElse方法的延遲呼叫版,Supplier方法只有在Optional物件不含值時才執行呼叫
7、map/flatMap/filter //與Stream中用法類似
三、完整的示例
這裡寫一個針對以上API都涉及到的Demo,這個例子明白了,那麼Optional的使用也就都清楚了。
程式碼
public class OptionalDemo {
public static void main(String[] args) {
//1、建立Optional例項,傳入的物件不能為null
Optional<String> nameOptional = Optional.of("張三");
//2、建立Optional例項,傳入物件可以為null,也可以不weinull
Optional emptyOptional = Optional.ofNullable(null);
//3、isPresent方法用來檢查Optional例項是否有值。
if (nameOptional.isPresent()) {
//呼叫get()返回Optional值。
System.out.println("1、" + nameOptional.get());
}
try {
//4、在Optional例項上呼叫get()丟擲NoSuchElementException。
System.out.println("2、" + emptyOptional.get());
} catch (NoSuchElementException ex) {
System.out.println("3、異常" + ex.getMessage());
}
//
//5、如果Optional值不為空,lambda表示式會處理並在其上執行操作。(這裡x代表就是nameOptional中的物件)
nameOptional.ifPresent((x) -> {
System.out.println("4、字串長度為: " + x.length());
});
//6、如果有值orElse方法會返回Optional例項,沒值則返回當前值
System.out.println("5、"+ emptyOptional.orElse("如果是空容器則返回李四"));
System.out.println("6、"+nameOptional.orElse("如果是空容器則返回王五"));
//7、orElseGet與orElse類似,區別在於傳入的引數不同,一個是直接傳入物件,這個是傳入Supplier函式式介面
System.out.println("7、" + emptyOptional.orElseGet(() -> "李四"));
System.out.println("8、" + nameOptional.orElseGet(() -> "王五"));
try {
//8、如果是空容器,則可以丟擲自定義異常。
emptyOptional.orElseThrow(() -> new NullPointerException("空容器異常"));
} catch (Throwable ex) {
System.out.println("9、" + ex.getMessage());
}
Optional<String> ageOptional = Optional.of("10");
//9、這裡入參是Function,所以可以轉換容器中的物件 好比將String物件轉為Integer物件
Optional<Integer> age = ageOptional.map((value) -> Integer.parseInt(value));
/**
* 10、flatMap與map(Funtion)非常相似,不同在於 map返回可以將String物件轉為Integer物件,但flatMap轉換後一定還是String物件
*/
Optional<String> upperName = nameOptional.flatMap((value) -> Optional.of(value.toUpperCase()));
//11、filter方法檢查Optiona值是否滿足給定條件。如果滿足返回Optional例項值,否則返回空Optional。
Optional<String> longName = nameOptional.filter((value) -> value.length() > 6);
System.out.println("10、" + longName.orElse("longName容器的名字長度小於6位"));
//12、另一個示例,Optional滿足給定條件。
Optional<String> anotherName = Optional.of("烏啦啦市長公主");
Optional<String> shortName = anotherName.filter((value) -> value.length() > 6);
System.out.println("11、" + shortName.orElse("anotherName容器的名字長度小於6位"));
}
}
執行結果
參考
1、JDK8新特性之:Optional
2、Optional類包含的方法介紹及其示例
你如果願意有所作為,就必須有始有終。(26)