null在Java中帶來的麻煩
我相信全部的Java程序猿必定都遇到過NullPointerException
,空指針在Java程序中是最多見的,也是最煩人的;它讓咱們不少程序猿產生了根深蒂固的感受,全部可能產生空指針的地方都的加上if-else
檢查,可是這帶給咱們不少麻煩java
- Java自己是強類型的,可是null破壞了這個規則,它能夠被賦值給任何對象
- Java的設計是讓程序猿對指針無感知,可是null指針是個例外
- 它會是代碼變得很臃腫,處處都充斥着
if-else
的空檢查,甚至是多層嵌套,代碼可讀性降低 - null自己毫無心義,表示不了
無
前兩點不須要特別的說明,後兩點舉個例子來講明一下: 假如一我的擁有一個手機,每一個手機都有生成廠商,每一個廠商都會有個名字,用類表示的話:app
public class Person { private Phone phone; public Phone getPhone() { return phone; } } public class Phone { private Producer producer; public Producer getProducer() { return producer; } } public class Producer { private String name; public String getName() { return name; } }
在這個例子中,假如咱們須要取到手機生成廠商的名字函數
public String getPhoneProducerName(Person person) { return person.getPhone().getProducer().getName(); }
因爲不必定每一個人都會有一個手機,全部在調用getProducer()
時可能會出現NullPointerException
。學習
一門設計語言原本就是來描述世界的,在這個事例中有的人有手機,有的人也可能沒有手機,因此在調用person.getPhone()
返回的值就應該包含有和無這兩種狀況,如今經過返回null
來表示無,可是在調用getProducer()
卻又會拋出異常,這樣就不太符合現實邏輯;因此把null
來用來表示無
不合適ui
在遇到這種狀況一般的作法是作null檢查,甚至是每一個地方可能發生null指針的作檢查。設計
public String getPhoneProducerName(Person person) { if (person.getPhone() == null) { return "無名字"; } if (person.getPhone().getProducer() == null) { return "無名字"; } return person.getPhone().getProducer().getName(); }
這裏我已經試圖在減小代碼的層級,若是使用的是if-else
,代碼的層級會更深,代碼可讀性降低。指針
Optional的簡單介紹
吐槽了那麼多現狀的很差,如今能夠祭出咱們的解決方案了 Optional
;千呼萬喚始出來,猶抱琵琶半遮面;那Optional
究竟是個什麼東西,咱們一塊兒來逐步解開它的面紗。code
Optional
自己只是對對象的簡單包裝,若是對象爲空,那麼會構建一個空的Optional
;這樣一來Optional
就包含了存在和不存在兩個狀況, 接下來能夠看下上面的例子改過以後對象
public class Person { private Optional<phone> phone; public Optional<phone> getPhone() { return phone; } } public class Phone { private Producer producer; public Producer getProducer() { return producer; } } public class Producer { private String name; public String getName() { return name; } }
因爲有的人可能沒有手機,有的人有,因此Phone
須要用Optional
包裝起來;手機自己必定會有生產的廠商,廠商必定會有一個名字,因此這兩個不須要用Optional
包裝起來。這裏咱們會發現使用了Optional
會豐富代碼的語義,讓代碼更加符合現實。blog
而當咱們在調用phone.getProducer().getName()
的時候不須要作null指針的檢查,若是說在這裏發生了NullPointerException
,說明這裏數據自己是有問題的,不符合現實,就應該讓問題暴露出來,而不是像上面的代碼同樣把問題掩蓋。
Optional的經常使用方法使用
1. Optional的建立方法
Optional<person> empty = Optional.empty(); //申明一個空的Optional Optional<person> person = Optional.of(new Person()); //包裝Person Optional<person> person2 = Optional.of(null); //不容許的操做,傳入null 會拋出空指針異常 Optional<person> optionalPerson = Optional.ofNullable(null); //容許傳null, 返回一個空的Optional
2. Optional值的獲取方式
- map、flatMap 首先咱們從新定義一下Phone類,除了有生產廠商以外,還有個型號;
public class Phone { private String model; private Producer producer; public Producer getProducer() { return producer; } public String getModel() { return model; } }
當咱們須要獲取到手機的型號的時候能夠這樣:
Optional<phone> optionalPhone = Optional.of(new Phone()); Optional<string> model = optionalPhone.map(Phone::getModel);
當咱們須要經過Person對象獲取到Phone的型號是,會想到這樣:
Optional<person> optionalPerson = Optional.of(new Person()); optionalPerson.map(Person::getPhone).map(Phone::getModel);
當咱們寫出來的時候發現編譯器不能經過。是由於Person::getPhone
返回的是一個Optional<phone>
,調用optionalPerson.map(Person::getPhone)
返回的就是Optional<optional<phone>>
,因此再.map
的就沒法拿到手機型號,那如何可以讓返回的結果不是Optional<optional<phone>>
,而是Optional<phone>
呢?
這裏須要用到另外一個方法flatMap
。flatMap
和map
的區別,我在剛開始學習的時候,看到了網上的各類解釋都很繞,看的很暈,最後直接打開源碼來看,發現實現很簡單,很容易理解,來看下源碼:
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)); } } 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
方法在返回的時候會包裝一層Optional
; flatMap
在返回的時候直接把函數的返回值返回了,函數的結果必須是Optional
;那麼在前面的例子中咱們直接調用flatMap
返回的結果就是Optional<phone>
Optional<person> optionalPerson = Optional.of(new Person()); optionalPerson.flatMap(Person::getPhone).map(Phone::getModel);
- 取出
Optional
中的值對象:get、orElse、orElseGet、orElseThrow、ifPresent
- get() : 當你明確知道Optional中有值的話能夠直接調用該方法,當Optional中沒有值是該方法會拋出異常
NoSuchElementException
;因此當若是存在空值的話建議就不要調用該方法,由於這樣和作null檢查就沒有區別了 - orElse(T other) : 提供一個默認值,當值不存在是返回這個默認值
- orElseGet(Supplier<!--? extends T--> other) : 當值不存在的時候會調用supper函數,若是說返回這個默認值的邏輯較多,那麼調用這個方法比較合適;
- orElseThrow(Supplier<!--? extends X--> exceptionSupplier) : 當值爲空時會拋出一個自定義的異常
- ifPresent(Consumer<!--? super T--> consumer) : 當值不爲空是會調用
consumer
函數,若是值爲空,那麼這個方法什麼都不作
- filter 過濾出知足條件的對象 假如咱們須要過濾出手機型號
IOS
的手機,並打印出型號,代碼以下:
Person person = new Person(Optional.of(new Phone("IOS"))); Optional<person> optionalPerson = Optional.of(person); optionalPerson.flatMap(Person::getPhone) .filter(phone -> "IOS".equals(phone.getModel())) .map(Phone::getModel) .ifPresent(System.out::println);
總結
- 咱們討論了null在Java程序的問題
- 介紹Java8中引入了
Optional
來表示有和無的狀況以及初始化的方式 - 舉例說明了
Optional
中常用到的方法
> 本人菜鳥,若是有任何寫的不對的地方,歡迎在評論區指出</person></person></phone></u></u></u></u></phone></optional<phone></optional<phone></phone></person></string></phone></person></person></person></person></phone></phone>