【JDK 8特性】 優雅的Optional空指針處理

樂猿社區,程序員的花果山java


那些年那NullPointerException

作爲一個javaer的老司機,能夠回憶一下,在咱的碼字生涯中到底遇到過多少次java.lang.NullPointerException異常?NullPointerException做爲一個RuntimeException級別的異常不用顯示捕獲,若不當心處理咱們常常會在生產日誌中看到各類由NullPointerException引發的異常堆棧輸出。程序員

咱們看看下面這個代碼,用很傳統很標準的Java編碼風格編寫:編程

String result = doSomething();
String [] str = result.split(",");

這一段代碼很簡單,平常的業務代碼確定比這個複雜的多,而實際上咱們大量的Java編碼都是按這種套路編寫的,可是若是doSomething返回的是一個null,那麼能夠看出最終確定會拋出NullPointerException。在咱們編寫業務代碼時,不多會想到要處理這個可能會出現的null,直到咱們到了某個測試階段,忽然蹦出一個NullPointerException異常,咱們才意識到原來咱們得像下面這樣加一個判斷來搞定這個可能會返回的null值。安全

String result = doSomething();
if(result!=null){
	String [] str = result.split(",");
}

利用Optional實現Java函數式編程

眼看像Scala等基於jvm的語言都有相似的語法糖了,java做爲一統江湖的老大哥,怎麼可能會沒有自已的必殺技呢?新版本的Java,好比Java 8引入了一個新的Optional類。Optional類的Javadoc描述以下:app

它是一個容器,裝載着非NULL元素(或者沒有裝載元素),提供了一系列的方法供咱們判斷該容器裏的對象是否存在(以及後續的操做)。less

對於上段代碼,採用正確姿式Optional後變身爲:jvm

String result = doSomething();
String[] str = Optional.ofNullable(result).map(str->str.split(",")).orElse(new String []{});

Optional經常使用必殺持

of

爲非null的值建立一個Optional。編程語言

of方法經過工廠方法建立Optional類。須要注意的是,建立對象時傳入的參數不能爲null。若是傳入參數爲null,則拋出NullPointerException函數式編程

/調用工廠方法建立Optional實例
Optional<String> name = Optional.of("樂猿社區");
//傳入參數爲null,拋出NullPointerException.
Optional<String> someNull = Optional.of(null);

ofNullable

爲指定的值建立一個Optional,若是指定的值爲null,則返回一個空的Optional。函數

ofNullable與of方法類似,惟一的區別是能夠接受參數爲null的狀況。示例以下:

//下面建立了一個不包含任何值的Optional實例
//例如,值爲'null'
Optional empty = Optional.ofNullable(null);

isPresent

若是值存在返回true,不然返回false

//isPresent方法用來檢查Optional實例中是否包含值
if (name.isPresent()) {
  //在Optional實例內調用get()返回已存在的值
  System.out.println(name.get());//輸出樂猿社區
}

get

若是Optional有值則將其返回,不然拋出NoSuchElementException

try {
  Optional empty = Optional.ofNullable(null);
  //在空的Optional實例上調用get(),拋出NoSuchElementException
  System.out.println(empty.get());
} catch (NoSuchElementException ex) {
  System.out.println(ex.getMessage());//輸出:No value present 
}

ifPresent

若是Optional實例有值則爲其調用consumer,不然不作處理

要理解ifPresent方法,首先須要瞭解Consumer類。簡答地說,Consumer類包含一個抽象方法。該抽象方法對傳入的值進行處理,但沒有返回值。Java8支持不用接口直接經過lambda表達式傳入參數。

若是Optional實例有值,調用ifPresent()能夠接受接口段或lambda表達式。相似下面的代碼:

//ifPresent方法接受lambda表達式做爲參數。
//lambda表達式對Optional的值調用consumer進行處理。
name.ifPresent((value) -> {
  System.out.println("The length of the value is: " + value.length());
});

orElse

若是有值則將其返回,不然返回指定的其它值。

若是Optional實例有值則將其返回,不然返回orElse方法傳入的參數。示例以下:

//若是值不爲null,orElse方法返回Optional實例的值。
//若是爲null,返回傳入的消息。
//輸出:There is no value present!
System.out.println(empty.orElse("There is no value present!"));
//輸出:Sanaulla
System.out.println(name.orElse("There is some value!"));

orElseGet

orElseGet與orElse方法相似,區別在於獲得的默認值。orElse方法將傳入的字符串做爲默認值,orElseGet方法能夠接受Supplier接口的實現用來生成默認值。示例以下:

//orElseGet與orElse方法相似,區別在於orElse傳入的是默認值,
//orElseGet能夠接受一個lambda表達式生成默認值。
//輸出:Default Value
System.out.println(empty.orElseGet(() -> "Default Value"));
//輸出:Sanaulla
System.out.println(name.orElseGet(() -> "Default Value"));

orElseThrow

若是有值則將其返回,不然拋出supplier接口建立的異常。

在orElseGet方法中,咱們傳入一個Supplier接口。然而,在orElseThrow中咱們能夠傳入一個lambda表達式或方法,若是值不存在來拋出異常。示例以下:

try {
  //orElseThrow與orElse方法相似。與返回默認值不一樣,
  //orElseThrow會拋出lambda表達式或方法生成的異常
  empty.orElseThrow(Exception::new);
} catch (Throwable ex) {
  //輸出: No value present in the Optional instance
  System.out.println(ex.getMessage());
}

map

若是有值,則對其執行調用mapping函數獲得返回值。若是返回值不爲null,則建立包含mapping返回值的Optional做爲map方法返回值,不然返回空Optional

map方法用來對Optional實例的值執行一系列操做。經過一組實現了Function接口的lambda表達式傳入操做。map方法示例以下:

//map方法執行傳入的lambda表達式參數對Optional實例的值進行修改。
//爲lambda表達式的返回值建立新的Optional實例做爲map方法的返回值。
Optional<String> lowerName = name.map((value) -> value.toLowerCase());
System.out.println(lowerName.orElse("No value found"));

flatMap

若是有值,爲其執行mapping函數返回Optional類型返回值,不然返回空Optional。

flatMap與map(Funtion)方法相似,區別在於flatMap中的mapper返回值必須是Optional。調用結束時,flatMap不會對結果用Optional封裝。

//map方法執行傳入的lambda表達式參數對Optional實例的值進行修改。
//爲lambda表達式的返回值建立新的Optional實例做爲map方法的返回值。
//但flatMap方法中的lambda表達式返回值必須是Optionl實例。
Optional<String> lowerName = name.flatMap((value) -> value.toLowerCase());
System.out.println(lowerName.orElse("No value found"));

filter

filter個方法經過傳入限定條件對Optional實例的值進行過濾

filter須要傳入一個lambda表達式。對於filter函數咱們應該傳入實現了Predicate接口的lambda表達式。

//filter方法檢查給定的Option值是否知足某些條件。
//若是知足則返回同一個Option實例,不然返回空Optional。
Optional<String> longName = name.filter((value) -> value.length() > 4);
System.out.println(longName.orElse("The name is less than 4 characters"));//輸出樂猿社區
 
//另外一個例子是Optional值不知足filter指定的條件。
Optional<String> anotherName = Optional.of("樂猿");
Optional<String> shortName = anotherName.filter((value) -> value.length() > 6);
//輸出:name長度不足4字符
System.out.println(shortName.orElse("The name is less than 6 characters"));

總結

經過filter,map 和 flatMap之類的函數能夠將其安全的進行變換,最後經過orElse系列,get,isPresent 和 ifPresent將其中的值提取出來,從而避免了NullPointerException,讓咱們的代碼看起來更加優雅

寫在最後的

Optional只是Java函數式編程的冰山一角,須要結合lambda、stream、Funcationinterface等特性才能真正的瞭解Java8函數式編程的效用。

鼓勵把新的Java8特性引入到目前的項目中,一個長期配合的團隊以及一門古老的編程語言都須要不斷的注入新活力,不然不進則退

相關文章
相關標籤/搜索