您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“如何使用Nacos中的Optional”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“如何使用Nacos中的Optional”吧!
在Nacos中有這樣一個(gè)接口ConsistencyService,用來(lái)定義一致性服務(wù)的,其中的一個(gè)方法返回的類型便是Optional:
/** * Get the error message of the consistency protocol. * * @return the consistency protocol error message. */ Optional<String> getErrorMsg();
如果你對(duì)Optional不了解,看到這里可能就會(huì)有點(diǎn)蒙。那我們來(lái)看看Nacos是怎么使用Optional的。在上述接口的一個(gè)實(shí)現(xiàn)類PersistentServiceProcessor中是如此實(shí)現(xiàn)的:
@Override public Optional<String> getErrorMsg() { String errorMsg; if (hasLeader && hasError) { errorMsg = "The raft peer is in error: " + jRaftErrorMsg; } else if (hasLeader && !hasError) { errorMsg = null; } else if (!hasLeader && hasError) { errorMsg = "Could not find leader! And the raft peer is in error: " + jRaftErrorMsg; } else { errorMsg = "Could not find leader!"; } return Optional.ofNullable(errorMsg); }
也就是根據(jù)hasLeader和hasError兩個(gè)變量來(lái)確定返回的errorMsg信息是什么。最后將errorMsg封裝到Optional中進(jìn)行返回。
下面再看看方法getErrorMsg是如何被調(diào)用的:
String errorMsg; if (ephemeralConsistencyService.getErrorMsg().isPresent() && persistentConsistencyService.getErrorMsg().isPresent()) { errorMsg = "'" + ephemeralConsistencyService.getErrorMsg().get() + "' in Distro protocol and '" + persistentConsistencyService.getErrorMsg().get() + "' in jRaft protocol"; }
可以看到在使用時(shí)只用先調(diào)用返回的Optional的isPresent方法判斷是否存在,再調(diào)用其get方法獲取即可。此時(shí)你可以回想一下如果不用Optional該如何實(shí)現(xiàn)。
到此,你可能有所疑惑用法,沒(méi)關(guān)系,下面我們就開始逐步講解Option的使用、原理和源碼。
面對(duì)新生事物我們都會(huì)有些許畏懼,當(dāng)我們庖丁解牛似的將其拆分之后,了解其實(shí)現(xiàn)原理,就沒(méi)那么恐怖了。
查看Optional類的源碼,可以看到它有兩個(gè)成員變量:
public final class Optional<T> { /** * Common instance for {@code empty()}. */ private static final Optional<?> EMPTY = new Optional<>(null); /** * If non-null, the value; if null, indicates no value is present */ private final T value; // ... }
其中EMPTY變量表示的是如果創(chuàng)建一個(gè)空的Optional實(shí)例,很顯然,在加載時(shí)已經(jīng)初始化了。而value是用來(lái)存儲(chǔ)我們業(yè)務(wù)中真正使用的對(duì)象,比如上面的errorMsg就是存儲(chǔ)在這里。
看到這里你是否意識(shí)到Optional其實(shí)就一個(gè)容器啊!對(duì)的,將Optional理解為容器就對(duì)了,然后這個(gè)容器呢,為我們封裝了存儲(chǔ)對(duì)象的非空判斷和獲取的API。
看到這里,是不是感覺(jué)Optional并沒(méi)那么神秘了?是不是也沒(méi)那么恐懼了?
而Java 8之所以引入Optional也是為了解決對(duì)象使用時(shí)為避免空指針異常的丑陋寫法問(wèn)題。類似如下代碼:
if( user != null){ Address address = user.getAddress(); if(address != null){ String province = address.getProvince(); } }
原來(lái)是為了封裝,原來(lái)是為了更優(yōu)雅的代碼,這不正是我們有志向的程序員所追求的么。
這么我們就姑且稱Optional為Optional容器了,下面就看看如何將對(duì)象放入Optional當(dāng)中。
看到上面的EMPTY初始化時(shí)調(diào)用了構(gòu)造方法,傳入null值,我們是否也可以這樣來(lái)封裝對(duì)象?好像不行,來(lái)看一下Optional的構(gòu)造方法:
private Optional() { this.value = null; } private Optional(T value) { this.value = Objects.requireNonNull(value); }
存在的兩個(gè)構(gòu)造方法都是private的,看來(lái)只能通過(guò)Optional提供的其他方法來(lái)封裝對(duì)象了,通常有以下方式。
empty方法
empty方法源碼如下:
// Returns an {@code Optional} with the specified present non-null value. public static <T> Optional<T> of(T value) { return new Optional<>(value); }
簡(jiǎn)單直接,直接強(qiáng)轉(zhuǎn)EMPTY對(duì)象。
of方法
of方法源碼如下:
public static <T> T requireNonNull(T obj) { if (obj == null) throw new NullPointerException(); return obj; }
注釋上說(shuō)是為非null的值創(chuàng)建一個(gè)Optional,而非null的是通過(guò)上面構(gòu)造方法中的Objects.requireNonNull方法來(lái)檢查的:
public static <T> T requireNonNull(T obj) { if (obj == null) throw new NullPointerException(); return obj; }
也就是說(shuō)如果值為null,則直接拋空指針異常。
ofNullable方法
ofNullable方法源碼如下:
public static <T> Optional<T> ofNullable(T value) { return value == null ? empty() : of(value); }
ofNullable為指定的值創(chuàng)建一個(gè)Optional,如果指定的值為null,則返回一個(gè)空的Optional。也就是說(shuō)此方法支持對(duì)象的null與非null構(gòu)造。
回顧一下:Optional構(gòu)造方法私有,不能被外部調(diào)用;empty方法創(chuàng)建空的Optional、of方法創(chuàng)建非空的Optional、ofNullable將兩者結(jié)合。是不是so easy?
此時(shí),有朋友可能會(huì)問(wèn),相對(duì)于ofNullable方法,of方法存在的意義是什么?在運(yùn)行過(guò)程中,如果不想隱藏NullPointerException,就是說(shuō)如果出現(xiàn)null則要立即報(bào)告,這時(shí)就用Of函數(shù)。另外就是已經(jīng)明確知道value不會(huì)為null的時(shí)候也可以使用。
上面已經(jīng)將對(duì)象放入Optional了,那么在獲取之前是否需要能判斷一下存放的對(duì)象是否為null呢?
isPresent方法
上述問(wèn)題,答案是:可以的。對(duì)應(yīng)的方法就是isPresent:
public boolean isPresent() { return value != null; }
實(shí)現(xiàn)簡(jiǎn)單直白,相當(dāng)于將obj != null的判斷進(jìn)行了封裝。該對(duì)象如果存在,方法返回true,否則返回false。
isPresent即判斷value值是否為空,而ifPresent就是在value值不為空時(shí),做一些操作:
public void ifPresent(Consumer<? super T> consumer) { if (value != null) consumer.accept(value); }
如果Optional實(shí)例有值則為其調(diào)用consumer,否則不做處理??梢灾苯訉ambda表達(dá)式傳遞給該方法,代碼更加簡(jiǎn)潔、直觀。
Optional<String> opt = Optional.of("程序新視界"); opt.ifPresent(System.out::println);
當(dāng)我們判斷Optional中有值時(shí)便可以進(jìn)行獲取了,像Nacos中使用的那樣,調(diào)用get方法:
public T get() { if (value == null) { throw new NoSuchElementException("No value present"); } return value; }
很顯然,如果value值為null,則該方法會(huì)拋出NoSuchElementException異常。這也是為什么我們?cè)谑褂脮r(shí)要先調(diào)用isPresent方法來(lái)判斷一下value值是否存在了。此處的設(shè)計(jì)稍微與初衷相悖。
看一下使用示例:
String name = null; Optional<String> opt = Optional.ofNullable(name); if(opt.isPresent()){ System.out.println(opt.get()); }
那么,針對(duì)上述value為null的情況是否有解決方案呢?我們可以配合設(shè)置(或獲取)默認(rèn)值來(lái)解決。
orElse方法
orElse方法:如果有值則將其返回,否則返回指定的其它值。
public T orElse(T other) { return value != null ? value : other; }
可以看到是get方法的加強(qiáng)版,get方法如果值為null直接拋異常,orElse則不,如果只為null,返回你傳入進(jìn)來(lái)的參數(shù)值。
使用示例:
Optional<Object> o1 = Optional.ofNullable(null); // 輸出orElse指定值 System.out.println(o1.orElse("程序新視界"));
orElseGet方法
orElseGet:orElseGet與orElse方法類似,區(qū)別在于得到的默認(rèn)值。orElse方法將傳入的對(duì)象作為默認(rèn)值,orElseGet方法可以接受Supplier接口的實(shí)現(xiàn)用來(lái)生成默認(rèn)值:
public T orElseGet(Supplier<? extends T> other) { return value != null ? value : other.get(); }
當(dāng)value為null時(shí)orElse直接返回傳入值,orElseGet返回Supplier實(shí)現(xiàn)類中定義的值。
String name = null; String newName = Optional.ofNullable(name).orElseGet(()->"程序新視界"); System.out.println(newName); // 輸出:程序新視界
其實(shí)上面的示例可以直接優(yōu)化為orElse,因?yàn)镾upplier接口的實(shí)現(xiàn)依舊是直接返回輸入值。
orElseThrow方法
orElseThrow:如果有值則將其返回,否則拋出Supplier接口創(chuàng)建的異常。
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X { if (value != null) { return value; } else { throw exceptionSupplier.get(); } }
使用示例:
Optional<Object> o = Optional.ofNullable(null); try { o.orElseThrow(() -> new Exception("異常")); } catch (Exception e) { System.out.println(e.getMessage()); }
學(xué)完上述內(nèi)容,基本上已經(jīng)掌握了Optional百分之八十的功能了。同時(shí),還有兩個(gè)相對(duì)高級(jí)點(diǎn)的功能:過(guò)濾值和轉(zhuǎn)換值。
filter方法過(guò)濾值
Optional中的值我們可以通過(guò)上面講的到方法進(jìn)行獲取,但在某些場(chǎng)景下,我們還需要判斷一下獲得的值是否符合條件。笨辦法時(shí),獲取值之后,自己再進(jìn)行檢查判斷。
當(dāng)然,也可以通過(guò)Optional提供的filter來(lái)進(jìn)行取出前的過(guò)濾:
public Optional<T> filter(Predicate<? super T> predicate) { Objects.requireNonNull(predicate); if (!isPresent()) return this; else return predicate.test(value) ? this : empty(); }
filter方法的參數(shù)類型為Predicate類型,可以將Lambda表達(dá)式傳遞給該方法作為條件,如果表達(dá)式的結(jié)果為false,則返回一個(gè)EMPTY的Optional對(duì)象,否則返回經(jīng)過(guò)過(guò)濾的Optional對(duì)象。
使用示例:
Optional<String> opt = Optional.of("程序新視界"); Optional<String> afterFilter = opt.filter(name -> name.length() > 4); System.out.println(afterFilter.orElse(""));
map方法轉(zhuǎn)換值
與filter方法類似,當(dāng)我們將值從Optional中取出之后,還進(jìn)行一步轉(zhuǎn)換,比如改為大寫或返回長(zhǎng)度等操作。當(dāng)然可以用笨辦法取出之后,進(jìn)行處理。
這里,Optional為我們提供了map方法,可以在取出之前就進(jìn)行操作:
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) { Objects.requireNonNull(mapper); if (!isPresent()) return empty(); else { return Optional.ofNullable(mapper.apply(value)); } }
map方法的參數(shù)類型為Function,會(huì)調(diào)用Function的apply方法對(duì)對(duì)Optional中的值進(jìn)行處理。如果Optional中的值本身就為null,則返回空,否則返回處理過(guò)后的值。
示例:
Optional<String> opt = Optional.of("程序新視界"); Optional<Integer> intOpt = opt.map(String::length); System.out.println(intOpt.orElse(0));
與map方法有這類似功能的方法為flatMap:
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) { Objects.requireNonNull(mapper); if (!isPresent()) return empty(); else { return Objects.requireNonNull(mapper.apply(value)); } }
可以看出,它與map方法的實(shí)現(xiàn)非常像,不同的是傳入的參數(shù)類型,map函數(shù)所接受的入?yún)㈩愋蜑镕unction,而flapMap的入?yún)㈩愋蜑镕unction>。
flapMap示例如下:
Optional<String> opt = Optional.of("程序新視界"); Optional<Integer> intOpt = opt.flatMap(name ->Optional.of(name.length())); System.out.println(intOpt.orElse(0));
對(duì)照map的示例,可以看出在flatMap中對(duì)結(jié)果進(jìn)行了一次Optional#of的操作。
到此,相信大家對(duì)“如何使用Nacos中的Optional”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。