在目前的工做中,我對Java中的Stream和Lambda表達式都使用得不少,以前也寫了兩篇文章來總結對應的知識。java
不過對於Optional這個特性,一直沒有很好地使用起來,因此最近又開始閱讀《Java 8實戰》這本書,本文是針對其中第10章的一個學習總結。面試
在Java中,若是你嘗試對null作函數調用,就會引起NullPointerException(NPE),NPE是Java程序開發中的最典型的異常,對於Java開發者來講,不管你是初出茅廬的新人和還工做多年的老司機,NPE常常讓他們翻車。爲了不NPE,他們會加不少if判斷語句,使得代碼的可讀性變得不好。數據庫
從軟件設計的角度來看,null自己是沒有意義的語義,這是一種對缺失變量值的錯誤的建模。編程
從Java類型系統的角度看,null能夠被賦值給任何類型的變量,而且不斷被傳遞,知道最後誰也不知道它是從哪裏引入的。後端
Java設計者從Haskell和Scala中獲取靈感,在Java 8中引入了一個新的類java.util.Optional<T>
。若是一個接口返回Optional,能夠表示一我的可能有車也可能沒有車,這個比簡單的返回Car要更明確,閱讀代碼的人不須要提早準備業務知識。設計模式
Optional的目的就在於此:經過類型系統讓你的領域模型中隱藏的知識顯式地體如今你的代碼中。安全
方法 | 描述 |
---|---|
empty | 返回一個空的Optional實例 |
filter | 若是值存在而且知足提供的過濾條件,則返回包含該值的Optional對象;不然就返回一個空的Optional對象 |
map | 若是值存在,就對該值執行提供的mapping函數調用 |
flatMap | 若是值存在,就對該值執行提供的mapping函數調用,返回一個Optional類型的值,不然就返回一個空的Optional對象 |
ifPresent | 若是值存在,就執行使用該值的方法調用,不然什麼也不作 |
of | 將指定值用Optional封裝以後返回,若是該值爲null,則拋出一個NPE |
ofNullable | 將指定值用Optional封裝以後返回,若是該值爲null,則返回一個空的Optional對象 |
orElse | 若是有值則返回,不然返回一個默認值 |
orElseGet | 若是有值則返回,不然返回一個由指定的Supplier接口生成的值(若是默認值的生成代價比較高的話,則適合使用orElseGet方法) |
orElseThrow | 若是有值則返回,不然返回一個由指定的Supplier接口拋出的異常 |
get | 若是值存在,則返回該值,不然拋出一個NoSuchElementException異常 |
isPresent | 若是值存在則返回true,不然返回false |
上面這張表裏列舉了Optional的基礎API,我這裏列舉了一些使用的tips:app
//empty方法的使用
Optional<Car> optCar = Optional.empty();
//of方法的使用
Optional<Car> optCar = Optional.of(car);
//ofNullable方法的使用
Optional<Car> optCar = Optional.ofNullable(car);
複製代碼
Optional<Insurance> optInsurance = Optional.ofNullable(insurance);
Optional<String> name = optInsurance.map(Insurance::getName);
複製代碼
//轉換以前
public String getCarInsuranceName(Person person) {
return person.getCar().getInsurance().getName();
}
//轉換後
public String getCarInsuranceName(Optional<Person> person) {
return person.flatMap(Person::getCar)
.flatMap(Car::Insurance)
.map(Insurance::getName)
.orElse("Unknown");
}
複製代碼
public class Person {
private Car car;
public Optional<Car> getCarAsOptional() {
return Optional.ofNullable(car);
}
}
複製代碼
Java方法處理異常結果的方式有兩種:返回null(或錯誤碼);拋出異常,例如:Integer.parseInt(String)這個方法——若是沒法解析到對應的整型,該方法就拋出一個NumberFormationException,這種狀況下咱們通常會使用try/catch語句處理異常狀況。函數式編程
通常咱們建議將try/catch塊單獨提取到一個方法中,在這裏使用Optional設計這個方法,代碼以下。在開發中,能夠嘗試構建一個OptionalUtility工具類,將這些複雜的try/catch邏輯封裝起來。函數
public static Optional<Integer> stringToInt(String a) {
try{
return Optional.of(Integer.parseInt(s));
} catch (NumberFormationException e) {
return Optional.empty();
}
}
複製代碼
如今有個方法,是嘗試從一個屬性映射中獲取某個關鍵詞對應的值,例子代碼以下:
public static int readDuration(Properties properties, String name) {
String value = properties.getProperty(name);
if (value != null) {
try {
int i = Integer.parseInt(value);
if (i > 0) {
return i;
}
} catch (NumberFormatException e) {
}
}
return 0;
}
複製代碼
使用Optional的寫法後,代碼以下所示:
public static int readDurationWithOptional(Properties properties, String name) {
return Optional.ofNullable(properties.getProperty(name))
.flatMap(OptionalUtility::stringToInt)
.filter(integer -> integer > 0)
.orElse(0);
}
複製代碼
若是須要訪問的屬性值不存在,Properites.getProperty(String)方法的返回值就是一個null,使用noNullable工廠方法就能夠將該值轉換爲Optional對象;接下來,可使用flatMap將一個Optional轉換爲Optional對象;最後使用filter過濾掉負數,而後就可使用orElse獲取屬性值,若是拿不到則返回默認值0。
使用Optional的思路和Stream相同,都是鏈式思路,跟數據庫查詢似的,表達力很強,並且省去了哪些複雜的try/catch和if-then-else方法。在後面的開發中,可使用Optional設計API,這樣能夠設計出更安全的接口和方法。
本號專一於後端技術、JVM問題排查和優化、Java面試題、我的成長和自我管理等主題,爲讀者提供一線開發者的工做和成長經驗,期待你能在這裏有所收穫。