java.util.Optional是Java 8新增的類,作為一個持有實例的容器類,可以幫我們把判空的代碼寫得更優雅,並且該類還提供了一些實用的api,官方文檔在這里,接下來我們通過實戰來學習吧:
三種Optional構造方法###
第一種. Optional.of(Object object):入參object不能為空,否則會拋出空指針異常,查看Optional源碼發現會調用Objects.requireNonNull方法,里面有判空:
public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
第二種. Optional.ofNullable(Object object):入參object可以為空,如果object不為空,就創建一個Optional實例;如果object為空就返回一個static fainal的Option對象,注意這里不會新建Option實例,而是使用一個static final的實例EMPTY,這里比較有意思的是泛型的問題,例如我需要兩個Optional對象,類型分別是String和Integer,代碼如下:
Optional<String> optionalStr = Optional.ofNullable(null);
Optional<Integer> optionalInt = Optional.ofNullable(null);
類型不同又如何保證返回同一個對象呢?直接看ofNullable的源碼,發現會調用empty方法:
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
原來是通過強制轉換實現的,再看EMPTY對象:
private static final Optional<?> EMPTY = new Optional<>();
是通過"?"聲明的;
第三種. Optional.empty():就是上面分析Optional.ofNullable的時候用到的empty方法,直接返回一個static final的實例EMPTY;
Optional.of()方法的用法有點像斷言,對象為空的時候代表着某種業務上不可接受的異常,需要盡早處理,並且業務拒絕執行,這種場景下可以使用Optional.of;
接下來我們開始實戰吧;
例子中用到的對象:Student###
Student是個普通的bean,有三個字段和對應的get&set方法
package com.bolingcavalry;
/**
* @author willzhao
* @version V1.0
* @Description: 一個普通的bean
* @email zq2599@gmail.com
* @Date 2017/8/26 下午11:23
*/
public class Student {
private int id;
private String name;
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Student(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
}
Optional.ofNullable的用法###
下面舉例說明最常用的Optional.ofNullable,我們打算根據名稱從其他系統獲取student對象,如果對象為空就返回默認對象,先看不用Optional的時候我們平常是怎么寫的,如下代碼所示,標准的if&else判斷:
private Student queryById(int id){
//TODO 這里模擬從數據庫查詢
return null;
}
public Student getStudent(int id){
Student student = queryById(id));
//如果為空就返回DEFAULT對象
return student==null ? DEFAULT : student;
}
用Optional之后,如下所示,不需要通過判空來避免空指針異常了:
private Student queryById(int id){
//TODO 這里模擬從數據庫查詢
return null;
}
public Student getStudent(int id){
Optional<Student> optional = Optional.ofNullable(queryById(id));
//如果為空就返回DEFAULT對象
return optional.orElse(DEFAULT);
}
orElse方法可以指定一個value為空時的返回對象,如果這個對象需要調用方法才能獲取(例如我們拿不到DEFAULT對象,要通過getDefault()方法才能拿到),這是就需要orElseGet方法來達到目的,如下:
private Student queryById(int id){
//TODO 這里模擬從數據庫查詢
return null;
}
private Student getDefault(){
return DEFAULT;
}
public Student getStudent(int id){
Optional<Student> optional = Optional.ofNullable(queryById(id));
//如果為空就返回DEFAULT對象
return optional.orElseGet(() -> getDefault());
}
Optional的map方法###
假如我們的需求是student對象非空就返回name的大寫,如果student對象為空就返回"invalid",在沒有Optional的時候寫法如下,除了檢查student變量是否為空,還要檢查name是否為空:
private Student queryById(int id){
//TODO 這里模擬從數據庫查詢
return null;
}
public String getStudentUpperName(int id){
Student student = queryById(id);
if(student!=null && student.getName()!=null){
return student.getName().toUpperCase();
}
return "invalid";
}
用了Optional可以這么寫:
private Student queryById(int id){
//TODO 這里模擬從數據庫查詢
return null;
}
public String getStudentUpperName(int id){
Optional<Student> optional = Optional.ofNullable(queryById(id));
return optional.map(student -> student.getName())
.map(name -> name.toUpperCase())
.orElse("invalid");
}
由以上代碼可以看到,map可以將一個Optional對象轉換成另一個,第一次是將Optional
本次實戰的源碼已經上傳到git上,地址是git@github.com:zq2599/blog_demos.git,里面有多個工程,本次用到的是optionaldemo,如下圖紅框所示:

以上就是Optional的基本用法,對Optional的使用是在習慣上對之前判空寫法的挑戰,但可以試着去習慣這個簡單優雅的小工具;
歡迎關注我的公眾號

