天天看點

牛逼,這2招,徹底讓你和 null say 拜拜

你好呀,我是沉默王二,一個和黃家駒一樣身高,和劉德華一樣顔值的程式員。從 10 年前我開始寫第一行 Java 代碼至今,一直覺得 null 在 Java 中是一個最特殊的存在,它既是好朋友,可以把不需要的變量置為 null 進而釋放記憶體,提高性能;它又是敵人,因為它和大名鼎鼎且煩不勝煩的 NullPointerException(NPE)如影随形,而 NPE 的發明人 Tony Hoare 曾在 2009 年承認:“Null References 是一個荒唐的設計,就好像我賭輸掉了十億美元”。

你看,null 竟然是一個亦敵亦友的家夥。

通常,為了表示清單中的元素不存在,我們首先想到的就是傳回 null,這種想法很合理,合理到無法反駁。我們來模拟一個實際的應用場景,假設小二現在要從資料庫中擷取一個姓名的清單,然後将姓名列印到控制台,對應的代碼如下。

public class NullDemo {
    public static void main(String[] args) {
        List<String> names = getNamesFromDB();
        if (names != null) {
            for (String name : names) {
                System.out.println(name);
            }
        }
    }
    public static List<String> getNamesFromDB() {
        // 模拟此時沒有從資料庫擷取到對應的姓名。
        return null;
    }
}      

由于 getNamesFromDB() 方法傳回了 null 來作為沒有姓名清單的标志,那就意味着在周遊清單的時候要先對清單判空,否則将會抛出 NPE 錯誤,不信你把 if (names != null) 去掉試試,立馬給你顔色看。

Exception in thread "main" java.lang.NullPointerException

at com.cmower.dzone.stopdoing3things.NullDemo.main(NullDemo.java:12)

那假如小二在周遊的時候不想判空又不想代碼抛出 NPE 錯誤,他該怎麼做呢?閉上你的大眼睛好好想一想。

嗯,報告,我想出來了,建議小二從資料庫中擷取姓名的時候傳回長度為 0 的清單,來表示未找到資料的情況。代碼示例如下所示:

public class Null2Length0Demo {
    public static void main(String[] args) {
        List<String> names = getNamesFromDB();
        for (String name : names) {
            System.out.println(name);
        }
    }
    public static List<String> getNamesFromDB() {
        // 模拟此時沒有從資料庫擷取到對應的姓名。
        return Collections.emptyList();
    }
}      

注:Collections.emptyList() 用于傳回一個不可變的空清單,能了解吧?假如不能了解的話,我再寫一個傳回可變的空清單的示例,你對比着感受一下就了解了。

public class Null2Length0MutableDemo {
    public static void main(String[] args) {
        List<String> names = getNamesFromDB();
        for (String name : names) {
            System.out.println(name);
        }
    }
    public static List<String> getNamesFromDB() {
        // 模拟此時沒有從資料庫擷取到對應的姓名。
        return new ArrayList<>();
    }
}      

new ArrayList<>() 傳回的就是可變的,意味着你還可以改變這個清單的元素,比如說增加,删除是不可能的了,因為本身就沒有元素可删。

你看,Collections.emptyList() 和 new ArrayList<>() 都可以替代 null,來減少列印清單時不必要的判空以及那個讨厭的家夥——NPE。

除了我這個想法之外,你還能想到其他的解決方案嗎?來,再次閉上你的大眼睛,替小二想一想,沒準你還能想到一個—— Java 8 新增的 Optional 類,一個容器類,可以存放任意類型的元素,如果值存在則

isPresent() 方法會傳回 true;Optional 類提供了很多專業的方法而不用顯式進行空值檢查,進而巧妙地消除了 NPE。

來,先讀示例為快!

public class Null2OptionalDemo {
    public static void main(String[] args) {
        Optional<List<String>> list = getNamesFromDB();
        list.ifPresent(names -> {
            for (String name : names) {
                System.out.println(name);
            }
        });
    }
    public static Optional<List<String>> getNamesFromDB() {
        boolean hasName = true;
        if (hasName) {
            String [] names = {"沉默王二", "一枚有趣的程式員", "微信搜尋關注我"};
            return Optional.of(Arrays.asList(names));
        }
        return Optional.empty();
    }
}      

看得不太懂?我來負責任地介紹一下,你們握個手。

假如資料庫中存在姓名,則使用 Optional.of() 對傳回值進行包裝,進而傳回一個 Optional 類型的對象。為什麼不用構造方法呢,因為構造方法是 private 的(源碼如下所示)。

private Optional(T value) {

   this.value = value;

}

那為什麼要用 Optional.of() 呢?嗯,good question。繼續上源碼。

public static <T> Optional<T> of(T value) {

   return new Optional<>(Objects.requireNonNull(value));

1)如果 value 為 null,那麼 Objects.requireNonNull(value) 就會抛出 NPE(嗯哼,總歸是要碰面的,但好歹不用我們程式員主動 check 了)。

2)如果 value 不為 null,則通過 new 關鍵字建立正常的 Optional 對象。

假如資料庫中不存在姓名呢?使用 Optional.empty() 作為傳回值。來,繼續上源碼。

public static<T> Optional<T> empty() {
    @SuppressWarnings("unchecked")
    Optional<T> t = (Optional<T>) EMPTY;
    return t;
}      

嗯哼,EMPTY 是什麼玩意?

private static final Optional<?> EMPTY = new Optional<>(null);

1

竟然是 Optional 類的一個私有常量(static + final)。怎麼此刻我的腦子裡想起了安徒生先生的寓言故事——皇帝的新衣,嗯,甭管了,反正“底層終究是醜陋的”。

這樣的話,就可以使用 Optional 對象的 ifPresent() 方法來判斷值是否存在,如果隻需要處理值存在的情況,就可以使用 Lambda 表達式的方式直接列印姓名。

list.ifPresent(names -> {

   for (String name : names) {

       System.out.println(name);

   }

});

有點簡單粗暴,對不對?但不管怎麼說,終于可以在表象上和 null,NPE 說拜拜了,做人嘛,開心點。

好了,我親愛的讀者朋友,以上就是本文的全部内容了,能看到這裡的就是最優秀的程式員。原創不易,莫要白票,請你為本文點贊個吧,這将是我寫作更多優質文章的最強動力。