一、Optional入門
Optional是jdk1.8引入的類型,Optional是一個容器對象,它包括了我們需要的對象,使用isPresent方法判斷所包含對象是否為空,isPresent方法返回false則表示Optional包含對象為空,否則可以使用get()取出對象進行操作。
之前的寫法:
public Person getPerson() { Person person = new Person(); if (null == person) { return null; } return person; }
現在可以寫成:
public Person getPerson() { Person person = new Person(); return Optional.ofNullable(person).orElse(null); }
其中Person類
@Data public class Person { private String name; private Integer age; }
Optional的優點是:
1、提醒你非空判斷。
2、將對象非空檢測標准化。
首先我們先打開Optional的內部,去一探究竟 先把幾個創建Optional對象的方法提取出來
public final class Optional<T> { private static final Optional<?> EMPTY = new Optional<>(); private final T value; //我們可以看到兩個構造方格都是private 私有的 //說明 我們沒辦法在外面去new出來Optional對象 private Optional() { this.value = null; } private Optional(T value) { this.value = Objects.requireNonNull(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); } //這個靜態方法大致是 如果參數value為空,則創建空對象,如果不為空,則創建有參對象 public static <T> Optional<T> ofNullable(T value) { return value == null ? empty() : of(value); } }
1、我們可以看到兩個構造方格都是private 私有的,說明我們沒辦法在外面去new出來Optional對象。
2、靜態方法:empty方法創建出一個包裝值為空的對象。of方法創建出一個包裝值非空的對象,ofNullable方法創建包裝對象值可以為空也可以不為空的對象。
// 1、創建一個包裝對象值為空的Optional對象 Optional<String> optEmpty = Optional.empty(); // 2、創建包裝對象值非空的Optional對象 Optional<String> optOf = Optional.of("optional"); // 3、創建包裝對象值允許為空也可以不為空的Optional對象 Optional<String> optOfNullable1 = Optional.ofNullable(null); Optional<String> optOfNullable2 = Optional.ofNullable("optional");
常用方法:
//of():為非null的值創建一個Optional Optional<String> optional = Optional.of("bam"); // isPresent(): 如果值存在返回true,否則返回false optional.isPresent(); // true //get():如果Optional有值則將其返回,否則拋出NoSuchElementException optional.get(); // "bam" //orElse():如果有值則將其返回,否則返回指定的其它值 optional.orElse("fallback"); // "bam" //ifPresent():如果Optional實例有值則為其調用consumer,否則不做處理 optional.ifPresent((s) -> System.out.println(s.charAt(0))); // "b"
使用案例1
//修改 @Test public void testUpdate() { Optional<CmsPage> optional = cmsPageRepository.findOne("5b17a34211fe5e2ee8c116c9"); if(optional.isPresent()){ CmsPage cmsPage = optional.get(); cmsPage.setPageName("測試頁面01"); cmsPageRepository.save(cmsPage); } }
使用案例2:判斷從JSONObject中取出的字符串是否為空
Object access_token = tokenResult.get("access_token"); String o = (String) Optional.ofNullable(access_token).orElse("");
簡寫:
String accessToken = (String) Optional.ofNullable(tokenResult.get("access_token")).orElse("");
實際開發中,這種判斷比較常見。我們經常會對取出的值進行判斷,如果為null,則賦值為空字符串,否則為本身。
二、Optional API
Optional提供很多有用的方法,這樣我們就不用顯式進行空值檢測。Optional 類的引入很好的解決空指針異常。
Optional API地址:https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html

static <T> Optional<T> ofNullable(T value):當value值非空時,則返回一個非空的Optional,否則返回一個空的Optional
<U> Optional<U> map(Function<? super T,? extends U> mapper):如果value值存在,則執行提供的映射函數,如果結果不為空,返回一個非空的Optional
T orElse(T other):如果value值存在則返回value值,如果不存在,則返回other。
<X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier):如果value值存在,則返回,如果不存在,則拋出一個由Supplier創建的異常。
void ifPresent(Consumer<? super T> consumer):如果value值存在,則調用Consumer接口(執行Lambda表達式或方法引用),否則什么都不做。
Optional<T> filter(Predicate<? super T> predicate):如果值存在且值匹配predicate,返回一個非空的Optional,否則返回一個空的Optional。
T orElseGet(Supplier<? extends T> other):如果值存在,則返回該值,否則調用other並返回調用的結果
1、Optional.get()方法(返回對象的值)
get()方法是返回一個option的實例值 源碼:
public T get() { if (value == null) { throw new NoSuchElementException("No value present"); } return value; }
例子:
// 1、創建一個包裝對象值為空的Optional對象 Optional<String> optEmpty = Optional.empty(); String s = optEmpty.get();
結果:java.util.NoSuchElementException: No value present
2、Optional.isPresent()方法(判讀是否為空)
isPresent()方法就是會返回一個boolean類型值,如果對象不為空則為真,如果為空則false 源碼:
public boolean isPresent() { return value != null; }
故在get之前需要調用isPresent方法進行判斷
Optional<String> optOf = Optional.of("optional"); if (optOf.isPresent()) { System.out.println(optOf.get()); }
例2:
Person person = new Person(); person.setAge(2); Optional<Person> optional = Optional.ofNullable(person); if (optional.isPresent()) { System.out.println("不為空"); }
結果:不為空
3、Optional.ifPresent()方法(判讀是否為空並返回函數)
這個意思是如果對象非空,則運行函數體 源碼:
public void ifPresent(Consumer<? super T> consumer) { if (value != null) consumer.accept(value); }
上面的代碼可以簡化如下:
Person person = new Person(); person.setAge(2); Optional.ofNullable(person).ifPresent(p-> System.out.println(p.getAge()));
結果:2
4、Optional.filter()方法(過濾對象)
filter()方法大致意思是,接受一個對象,然后對他進行條件過濾,如果條件符合則返回Optional對象本身,如果不符合則返回空Optional
源碼:
public Optional<T> filter(Predicate<? super T> predicate) { Objects.requireNonNull(predicate); //如果為空直接返回this if (!isPresent()) return this; else //判斷返回本身還是空Optional return predicate.test(value) ? this : empty(); }
簡單實例:
Person person = new Person(); person.setAge(2); Optional<Person> optional = Optional.ofNullable(person).filter(person1 -> person1.getAge() > 1); optional.ifPresent(p -> System.out.println(p.getAge()));
結果:2
5、Optional.map()方法(對象進行二次包裝)
map()方法將對應Funcation函數式接口中的對象,進行二次運算,封裝成新的對象然后返回在Optional中 源碼:
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) { Objects.requireNonNull(mapper); //如果為空返回自己 if (!isPresent()) return empty(); else { //否則返回用方法修飾過的Optional return Optional.ofNullable(mapper.apply(value)); } }
例子
Person person = new Person(); person.setAge(2); Integer integer = Optional.ofNullable(person).map(Person::getAge).orElse(null); System.out.println(integer);
結果:2
6、Optional.orElse()方法(為空返回對象)
常用方法之一,這個方法意思是如果包裝對象為空的話,就執行orElse方法里的value,如果非空,則返回寫入對象 源碼:
public T orElse(T other) { //如果非空,返回value,如果為空,返回other return value != null ? value : other; }
7、Optional.orElseGet()方法(為空返回Supplier對象)
這個與orElse很相似,入參不一樣,入參為Supplier對象,為空返回傳入對象的.get()方法,如果非空則返回當前對象 源碼:
Person person = new Person(); person.setAge(2); Optional<Supplier<Person>> sup=Optional.ofNullable(Person::new); //調用get()方法,此時才會調用對象的構造方法,即獲得到真正對象 Person person1 = Optional.ofNullable(person).orElseGet(sup.get()); System.out.println(person1);
結果:Person(name=null, age=2)
Person person = null; Optional<Supplier<Person>> sup=Optional.ofNullable(Person::new); //調用get()方法,此時才會調用對象的構造方法,即獲得到真正對象 Person person1 = Optional.ofNullable(person).orElseGet(sup.get()); System.out.println(person1);
結果:Person(name=null, age=null)
8、Optional.orElseThrow()方法(為空返回異常)
這個我個人在實戰中也經常用到這個方法,方法作用的話就是如果為空,就拋出你定義的異常,如果不為空返回當前對象,在實戰中所有異常肯定是要處理好的,為了代碼的可讀性。源碼:
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X { if (value != null) { return value; } else { throw exceptionSupplier.get(); } }
實例:這個就貼實戰源碼了
Person person = null; Optional.ofNullable(person).orElseThrow(() -> new RuntimeException("沒有查詢到相關數據"));
結果:
java.lang.RuntimeException: 沒有查詢到相關數據
三、Optional API的應用
善用 Optional 可以使我們代碼中很多繁瑣、丑陋的設計變得十分優雅。
使用 Optional,我們就可以把下面這樣的代碼進行改寫。
public static String getName(User u) { if (u == null || u.name == null) return "Unknown"; return u.name; }
不過,千萬不要改寫成這副樣子。
public static String getName(User u) { Optional<User> user = Optional.ofNullable(u); // 使用Optional包裝 if (!user.isPresent()) return "Unknown"; return user.get().name; }
這樣改寫非但不簡潔,而且其操作還是和第一段代碼一樣。無非就是用 isPresent 方法來替代 u==null。這樣的改寫並不是 Optional 正確的用法,我們再來改寫一次。
public static String getName(User u) { return Optional.ofNullable(u) .map(user->user.name) .orElse("Unknown"); }
這樣才是正確使用 Optional 的姿勢。那么按照這種思路,我們可以安心的進行鏈式調用,而不是一層層判斷了。看一段代碼:
public static String getChampionName(Competition comp) throws IllegalArgumentException { if (comp != null) { CompResult result = comp.getResult(); if (result != null) { User champion = result.getChampion(); if (champion != null) { return champion.getName(); } } } throw new IllegalArgumentException("The value of param comp isn't available."); }
讓我們看看經過 Optional 加持過后,這些代碼會變成什么樣子。
public static String getChampionName(Competition comp) throws IllegalArgumentException { return Optional.ofNullable(comp) .map(Competition::getResult) // 相當於c -> c.getResult(),下同 .map(CompResult::getChampion) .map(User::getName) .orElseThrow(()->new IllegalArgumentException("The value of param comp isn't available.")); }
還有很多不錯的使用姿勢,比如字符串為空則不打印可以這么寫:
string.ifPresent(System.out::println);
Optional 的魅力還不止於此,Optional 還有一些神奇的用法,比如 Optional 可以用來檢驗參數的合法性。
public void setName(String name) throws IllegalArgumentException { this.name = Optional.ofNullable(name) .filter(User::isNameValid) .orElseThrow(()->new IllegalArgumentException("Invalid username.")); }
這樣寫參數合法性檢測,應該足夠優雅了吧。
不過這還沒完,上面的兩個例子其實還不能完全反應出 Optional 的設計意圖。事實上,我們應該更進一步,減少 Optional.ofNullable 的使用。為什么呢?因為 Optional 是被設計成用來代替 null 以表示不確定性的,換句話說,只要一段代碼可能產生 null,那它就可以返回 Optional。而我們選擇用 Optional 代替 null 的原因,是 Optional 提供了一個把若干依賴前一步結果的處理結合在一起的途徑。
Optional應用建議
Optional 就像一個處理不確定性的管道,我們在一頭丟進一個可能是 null 的東西(接口返回結果),經過層層處理,最后消除不確定性。Optional 在過程中保留了不確定性,從而把對 null 的處理移到了若干次操作的最后,以減少出現 NPE 錯誤的可能。於是,Optional 應用的建議也呼之欲出了:
-
適用於層級處理(依賴上一步操作)的場合。
-
產生對象的方法若可能返回 null,可以用 Optional 包裝。
-
盡可能延后處理 null 的時機,在過程中使用 Optional 保留不確定性。
-
盡量避免使用 Optional 作為字段類型。
