Java中NullPointerException的完美解決方案

java.png

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

image

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>&gt;,因此再.map的就沒法拿到手機型號,那如何可以讓返回的結果不是Optional<optional<phone>&gt;,而是Optional<phone>呢?

這裏須要用到另外一個方法flatMapflatMapmap的區別,我在剛開始學習的時候,看到了網上的各類解釋都很繞,看的很暈,最後直接打開源碼來看,發現實現很簡單,很容易理解,來看下源碼:

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-->&gt; mapper) {
    Objects.requireNonNull(mapper);
    if (!isPresent())
        return empty();
    else {
        return Objects.requireNonNull(mapper.apply(value));
    }
}

map方法在返回的時候會包裝一層OptionalflatMap在返回的時候直接把函數的返回值返回了,函數的結果必須是Optional;那麼在前面的例子中咱們直接調用flatMap返回的結果就是Optional<phone>

Optional<person> optionalPerson = Optional.of(new Person());
optionalPerson.flatMap(Person::getPhone).map(Phone::getModel);
  • 取出Optional中的值對象:get、orElse、orElseGet、orElseThrow、ifPresent
  1. get() : 當你明確知道Optional中有值的話能夠直接調用該方法,當Optional中沒有值是該方法會拋出異常NoSuchElementException;因此當若是存在空值的話建議就不要調用該方法,由於這樣和作null檢查就沒有區別了
  2. orElse(T other) : 提供一個默認值,當值不存在是返回這個默認值
  3. orElseGet(Supplier<!--? extends T--> other) : 當值不存在的時候會調用supper函數,若是說返回這個默認值的邏輯較多,那麼調用這個方法比較合適;
  4. orElseThrow(Supplier<!--? extends X--> exceptionSupplier) : 當值爲空時會拋出一個自定義的異常
  5. 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 -&gt; "IOS".equals(phone.getModel()))
                .map(Phone::getModel)
                .ifPresent(System.out::println);

總結

  1. 咱們討論了null在Java程序的問題
  2. 介紹Java8中引入了Optional來表示有和無的狀況以及初始化的方式
  3. 舉例說明了Optional中常用到的方法

> 本人菜鳥,若是有任何寫的不對的地方,歡迎在評論區指出</person></person></phone></u></u></u></u></phone></optional<phone></optional<phone></phone></person></string></phone></person></person></person></person></phone></phone>

相關文章
相關標籤/搜索