自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Nacos中已經(jīng)有Optional使用案例了,是時候慎重對待這一語法了

開發(fā) 后端
Java 8提供了很多新特性,但很多朋友對此并不重視,依舊采用老的寫法。最近個人在大量閱讀開源框架的源碼,發(fā)現(xiàn)Java 8的很多API已經(jīng)被頻繁的使用了。

[[398310]]

本文轉(zhuǎn)載自微信公眾號「程序新視界」,作者二師兄。轉(zhuǎn)載本文請聯(lián)系程序新視界公眾號。

前言

Java 8提供了很多新特性,但很多朋友對此并不重視,依舊采用老的寫法。最近個人在大量閱讀開源框架的源碼,發(fā)現(xiàn)Java 8的很多API已經(jīng)被頻繁的使用了。

以Nacos框架為例,已經(jīng)有很典型的Optional使用案例了,而且場景把握的非常好。如果此時你還沒意識到要學(xué)習(xí)了解一下,以后看源代碼可能都有些費勁了。

今天這篇文章我們就基于Nacos中對Optional的使用作為案例,來深入講解一下Optional的使用。老規(guī)矩,源碼、案例、實戰(zhàn),一樣不少。

Nacos中的Optional使用

在Nacos中有這樣一個接口ConsistencyService,用來定義一致性服務(wù)的,其中的一個方法返回的類型便是Optional:

  1. /** 
  2.  * Get the error message of the consistency protocol. 
  3.  * 
  4.  * @return the consistency protocol error message. 
  5.  */ 
  6. Optional<String> getErrorMsg(); 

如果你對Optional不了解,看到這里可能就會有點蒙。那我們來看看Nacos是怎么使用Optional的。在上述接口的一個實現(xiàn)類PersistentServiceProcessor中是如此實現(xiàn)的:

  1. @Override 
  2. public Optional<String> getErrorMsg() { 
  3.     String errorMsg; 
  4.     if (hasLeader && hasError) { 
  5.         errorMsg = "The raft peer is in error: " + jRaftErrorMsg; 
  6.     } else if (hasLeader && !hasError) { 
  7.         errorMsg = null
  8.     } else if (!hasLeader && hasError) { 
  9.         errorMsg = "Could not find leader! And the raft peer is in error: " + jRaftErrorMsg; 
  10.     } else { 
  11.         errorMsg = "Could not find leader!"
  12.     } 
  13.     return Optional.ofNullable(errorMsg); 

也就是根據(jù)hasLeader和hasError兩個變量來確定返回的errorMsg信息是什么。最后將errorMsg封裝到Optional中進(jìn)行返回。

下面再看看方法getErrorMsg是如何被調(diào)用的:

  1. String errorMsg; 
  2. if (ephemeralConsistencyService.getErrorMsg().isPresent() 
  3.         && persistentConsistencyService.getErrorMsg().isPresent()) { 
  4.     errorMsg = "'" + ephemeralConsistencyService.getErrorMsg().get() + "' in Distro protocol and '" 
  5.             + persistentConsistencyService.getErrorMsg().get() + "' in jRaft protocol"

可以看到在使用時只用先調(diào)用返回的Optional的isPresent方法判斷是否存在,再調(diào)用其get方法獲取即可。此時你可以回想一下如果不用Optional該如何實現(xiàn)。

到此,你可能有所疑惑用法,沒關(guān)系,下面我們就開始逐步講解Option的使用、原理和源碼。

Optional的數(shù)據(jù)結(jié)構(gòu)

面對新生事物我們都會有些許畏懼,當(dāng)我們庖丁解牛似的將其拆分之后,了解其實現(xiàn)原理,就沒那么恐怖了。

查看Optional類的源碼,可以看到它有兩個成員變量:

  1. public final class Optional<T> { 
  2.     /** 
  3.      * Common instance for {@code empty()}. 
  4.      */ 
  5.     private static final Optional<?> EMPTY = new Optional<>(null); 
  6.  
  7.     /** 
  8.      * If non-null, the value; if null, indicates no value is present 
  9.      */ 
  10.     private final T value; 
  11.     // ... 

其中EMPTY變量表示的是如果創(chuàng)建一個空的Optional實例,很顯然,在加載時已經(jīng)初始化了。而value是用來存儲我們業(yè)務(wù)中真正使用的對象,比如上面的errorMsg就是存儲在這里。

看到這里你是否意識到Optional其實就一個容器啊!對的,將Optional理解為容器就對了,然后這個容器呢,為我們封裝了存儲對象的非空判斷和獲取的API。

看到這里,是不是感覺Optional并沒那么神秘了?是不是也沒那么恐懼了?

而Java 8之所以引入Optional也是為了解決對象使用時為避免空指針異常的丑陋寫法問題。類似如下代碼:

  1. if( user != null){ 
  2.     Address address = user.getAddress(); 
  3.     if(address != null){ 
  4.         String province = address.getProvince(); 
  5.     } 

原來是為了封裝,原來是為了更優(yōu)雅的代碼,這不正是我們有志向的程序員所追求的么。

如何將對象存入Optional容器中

這么我們就姑且稱Optional為Optional容器了,下面就看看如何將對象放入Optional當(dāng)中。

看到上面的EMPTY初始化時調(diào)用了構(gòu)造方法,傳入null值,我們是否也可以這樣來封裝對象?好像不行,來看一下Optional的構(gòu)造方法:

  1. private Optional() { 
  2.     this.value = null
  3.  
  4. private Optional(T value) { 
  5.     this.value = Objects.requireNonNull(value); 

存在的兩個構(gòu)造方法都是private的,看來只能通過Optional提供的其他方法來封裝對象了,通常有以下方式。

empty方法

empty方法源碼如下:

  1. // Returns an {@code Optional} with the specified present non-null value. 
  2. public static <T> Optional<T> of(T value) { 
  3.     return new Optional<>(value); 

簡單直接,直接強轉(zhuǎn)EMPTY對象。

of方法

of方法源碼如下:

  1. public static <T> T requireNonNull(T obj) { 
  2.     if (obj == null
  3.         throw new NullPointerException(); 
  4.     return obj; 

注釋上說是為非null的值創(chuàng)建一個Optional,而非null的是通過上面構(gòu)造方法中的Objects.requireNonNull方法來檢查的:

  1. public static <T> T requireNonNull(T obj) { 
  2.     if (obj == null
  3.         throw new NullPointerException(); 
  4.     return obj; 

也就是說如果值為null,則直接拋空指針異常。

ofNullable方法

ofNullable方法源碼如下:

  1. public static <T> Optional<T> ofNullable(T value) { 
  2.     return value == null ? empty() : of(value); 

ofNullable為指定的值創(chuàng)建一個Optional,如果指定的值為null,則返回一個空的Optional。也就是說此方法支持對象的null與非null構(gòu)造。

回顧一下:Optional構(gòu)造方法私有,不能被外部調(diào)用;empty方法創(chuàng)建空的Optional、of方法創(chuàng)建非空的Optional、ofNullable將兩者結(jié)合。是不是so easy?

此時,有朋友可能會問,相對于ofNullable方法,of方法存在的意義是什么?在運行過程中,如果不想隱藏NullPointerException,就是說如果出現(xiàn)null則要立即報告,這時就用Of函數(shù)。另外就是已經(jīng)明確知道value不會為null的時候也可以使用。

判斷對象是否存在

上面已經(jīng)將對象放入Optional了,那么在獲取之前是否需要能判斷一下存放的對象是否為null呢?

isPresent方法

上述問題,答案是:可以的。對應(yīng)的方法就是isPresent:

  1. public boolean isPresent() { 
  2.     return value != null

實現(xiàn)簡單直白,相當(dāng)于將obj != null的判斷進(jìn)行了封裝。該對象如果存在,方法返回true,否則返回false。

isPresent即判斷value值是否為空,而ifPresent就是在value值不為空時,做一些操作:

  1. public void ifPresent(Consumer<? super T> consumer) { 
  2.     if (value != null
  3.         consumer.accept(value); 

如果Optional實例有值則為其調(diào)用consumer,否則不做處理??梢灾苯訉ambda表達(dá)式傳遞給該方法,代碼更加簡潔、直觀。

  1. Optional<String> opt = Optional.of("程序新視界"); 
  2. opt.ifPresent(System.out::println); 

獲取值

當(dāng)我們判斷Optional中有值時便可以進(jìn)行獲取了,像Nacos中使用的那樣,調(diào)用get方法:

  1. public T get() { 
  2.     if (value == null) { 
  3.         throw new NoSuchElementException("No value present"); 
  4.     } 
  5.     return value; 

很顯然,如果value值為null,則該方法會拋出NoSuchElementException異常。這也是為什么我們在使用時要先調(diào)用isPresent方法來判斷一下value值是否存在了。此處的設(shè)計稍微與初衷相悖。

看一下使用示例:

  1. String name = null
  2. Optional<String> opt = Optional.ofNullable(name); 
  3. if(opt.isPresent()){ 
  4.     System.out.println(opt.get()); 

設(shè)置(或獲取)默認(rèn)值

那么,針對上述value為null的情況是否有解決方案呢?我們可以配合設(shè)置(或獲取)默認(rèn)值來解決。

orElse方法

orElse方法:如果有值則將其返回,否則返回指定的其它值。

  1. public T orElse(T other) { 
  2.     return value != null ? value : other; 

可以看到是get方法的加強版,get方法如果值為null直接拋異常,orElse則不,如果只為null,返回你傳入進(jìn)來的參數(shù)值。

使用示例:

  1. Optional<Object> o1 = Optional.ofNullable(null); 
  2. // 輸出orElse指定值 
  3. System.out.println(o1.orElse("程序新視界")); 

orElseGet方法

orElseGet:orElseGet與orElse方法類似,區(qū)別在于得到的默認(rèn)值。orElse方法將傳入的對象作為默認(rèn)值,orElseGet方法可以接受Supplier接口的實現(xiàn)用來生成默認(rèn)值:

  1. public T orElseGet(Supplier<? extends T> other) { 
  2.     return value != null ? value : other.get(); 

當(dāng)value為null時orElse直接返回傳入值,orElseGet返回Supplier實現(xiàn)類中定義的值。

  1. String name = null
  2. String newName = Optional.ofNullable(name).orElseGet(()->"程序新視界"); 
  3. System.out.println(newName); // 輸出:程序新視界 

其實上面的示例可以直接優(yōu)化為orElse,因為Supplier接口的實現(xiàn)依舊是直接返回輸入值。

orElseThrow方法

orElseThrow:如果有值則將其返回,否則拋出Supplier接口創(chuàng)建的異常。

  1. public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X { 
  2.     if (value != null) { 
  3.         return value; 
  4.     } else { 
  5.         throw exceptionSupplier.get(); 
  6.     } 

使用示例:

  1. Optional<Object> o = Optional.ofNullable(null); 
  2. try { 
  3.   o.orElseThrow(() -> new Exception("異常")); 
  4. } catch (Exception e) { 
  5.   System.out.println(e.getMessage()); 

學(xué)完上述內(nèi)容,基本上已經(jīng)掌握了Optional百分之八十的功能了。同時,還有兩個相對高級點的功能:過濾值和轉(zhuǎn)換值。

filter方法過濾值

Optional中的值我們可以通過上面講的到方法進(jìn)行獲取,但在某些場景下,我們還需要判斷一下獲得的值是否符合條件。笨辦法時,獲取值之后,自己再進(jìn)行檢查判斷。

當(dāng)然,也可以通過Optional提供的filter來進(jìn)行取出前的過濾:

  1. public Optional<T> filter(Predicate<? super T> predicate) { 
  2.     Objects.requireNonNull(predicate); 
  3.     if (!isPresent()) 
  4.         return this; 
  5.     else 
  6.         return predicate.test(value) ? this : empty(); 

filter方法的參數(shù)類型為Predicate類型,可以將Lambda表達(dá)式傳遞給該方法作為條件,如果表達(dá)式的結(jié)果為false,則返回一個EMPTY的Optional對象,否則返回經(jīng)過過濾的Optional對象。

使用示例:

  1. Optional<String> opt = Optional.of("程序新視界"); 
  2. Optional<String> afterFilter = opt.filter(name -> name.length() > 4); 
  3. System.out.println(afterFilter.orElse("")); 

map方法轉(zhuǎn)換值

與filter方法類似,當(dāng)我們將值從Optional中取出之后,還進(jìn)行一步轉(zhuǎn)換,比如改為大寫或返回長度等操作。當(dāng)然可以用笨辦法取出之后,進(jìn)行處理。

這里,Optional為我們提供了map方法,可以在取出之前就進(jìn)行操作:

  1. public<U> Optional<U> map(Function<? super T, ? extends U> mapper) { 
  2.     Objects.requireNonNull(mapper); 
  3.     if (!isPresent()) 
  4.         return empty(); 
  5.     else { 
  6.         return Optional.ofNullable(mapper.apply(value)); 
  7.     } 

map方法的參數(shù)類型為Function,會調(diào)用Function的apply方法對對Optional中的值進(jìn)行處理。如果Optional中的值本身就為null,則返回空,否則返回處理過后的值。

示例:

  1. Optional<String> opt = Optional.of("程序新視界"); 
  2. Optional<Integer> intOpt = opt.map(String::length); 
  3. System.out.println(intOpt.orElse(0)); 

與map方法有這類似功能的方法為flatMap:

  1. public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) { 
  2.     Objects.requireNonNull(mapper); 
  3.     if (!isPresent()) 
  4.         return empty(); 
  5.     else { 
  6.         return Objects.requireNonNull(mapper.apply(value)); 
  7.     } 

可以看出,它與map方法的實現(xiàn)非常像,不同的是傳入的參數(shù)類型,map函數(shù)所接受的入?yún)㈩愋蜑镕unction,而flapMap的入?yún)㈩愋蜑镕unction>。

flapMap示例如下:

  1. Optional<String> opt = Optional.of("程序新視界"); 
  2. Optional<Integer> intOpt = opt.flatMap(name ->Optional.of(name.length())); 
  3. System.out.println(intOpt.orElse(0)); 

對照map的示例,可以看出在flatMap中對結(jié)果進(jìn)行了一次Optional#of的操作。

小結(jié)

本文我們從Nacos中使用Optional的使用出發(fā),逐步剖析了Optional的源碼、原理和使用。此時再回頭看最初的示例是不是已經(jīng)豁然開朗了?

關(guān)于Optional的學(xué)習(xí)其實把握住本質(zhì)就可以了:Optional本質(zhì)上是一個對象的容器,將對象存入其中之后,可以幫我們做一些非空判斷、取值、過濾、轉(zhuǎn)換等操作。

理解了本質(zhì),如果哪個API的使用不確定,看一下源碼就可以了。此時,可以愉快的繼續(xù)看源碼了~  

 

責(zé)任編輯:武曉燕 來源: 程序新視界
相關(guān)推薦

2009-04-09 08:51:40

Windows 7微軟操作系統(tǒng)

2010-03-31 15:35:35

云計算

2020-09-27 06:47:20

5G網(wǎng)絡(luò)運營商

2015-09-18 16:11:04

圖標(biāo)桌面環(huán)境KDE

2023-06-06 07:41:00

Reacthook

2015-07-14 10:18:42

Windows 10正式版

2023-06-02 07:04:24

宏碁映泰技嘉

2024-02-20 12:30:36

AI模型

2018-10-18 09:58:41

物聯(lián)網(wǎng)IOT數(shù)字化

2023-02-06 14:41:13

量子模型

2022-12-27 14:45:55

量子計算

2013-08-20 15:27:59

Linux操作系統(tǒng)

2012-03-17 21:45:02

JavaScript

2023-10-19 15:25:40

2024-02-22 16:50:50

2017-02-17 07:46:29

2018-08-21 05:12:10

2024-01-02 07:34:38

CentOSLinuxRedhat

2018-07-27 16:58:07

人工智能機(jī)器學(xué)習(xí)機(jī)器人

2015-06-15 11:05:13

DCIM數(shù)據(jù)中心
點贊
收藏

51CTO技術(shù)棧公眾號