1. 程式人生 > >你真的開始用JDK8了嗎?(上)

你真的開始用JDK8了嗎?(上)

JDK8正式版已經發布三年了,JDK9預計將於今年9月釋出。很多應用都已經升級到了jdk8,java的大部分開源框架也早已支援jdk8,但是你真正開始使用jdk8了嗎?jdk8給你的程式碼帶來哪些改變?今天我們來梳理下,JDK8的特性給我們的程式碼帶來哪些改變?

Optional

NullPointerExceptions是在呼叫其他介面的時候,必須要考慮的問題。在業務程式碼中充斥著很多if(user!=null)..這樣的判斷空的程式碼。在Jdk8中提供了Optional來幫助我們優雅的解決NullPointerExceptions問題。

使用示例

我們來看下面一個示例:

一個使用者包含手機號和固定電話,輸入一個使用者物件,獲取這個使用者的聯絡方式,如果手機號不為空,則為手機號,否則如果固定電話不為空,則返回固定電話,否則預設返回“000000”

我們先來看下在jdk8以前的實現:

User user = getUser();

if (user == null) {
    return "000000";

if (user.getMobilePhone() != null 
    && !user.getMobilePhone().isEmpty()) {
    return user.getMobilePhone();
}

if (user.getPhone() != null 
    && !user.getPhone().isEmpty()) {
    return user.getPhone();
}

return
"000000";

我們接著看下使用Optional以後的用法:

Optional<User> user = Optional.ofNullable(getUser());

Optional<String> mobilePhone = 
         user.flatMap((u) -> Optional.ofNullable(u.getMobilePhone()))
        .filter(s->!s.isEmpty());

Optional<String> phone = 
        user.flatMap((u) -> Optional.ofNullable(u.getPhone()))
        .filter(s->!s.isEmpty());

return
mobilePhone.orElse(phone.orElse("000000"));

相信大多數開發者覺得第一種方式更容易看懂,而且對於這個需求來說,大部分開發者的實現也是第一種方式。因為第一種方式是大家從剛學程式語言開發,最熟悉的方式。從第一種方式轉向使用Optional來實現本需求時,是一種思維的轉變。從以往的null的方式轉向Optional這種新的方式。那麼這兩種方式的思維有哪些改變呢?

第一種方式,是一種命令式的思維方式,這是我們從學程式設計的第一天起就熟悉的過程式的思維,程式執行了什麼樣的步驟,得到了什麼樣的結果。我們可以通過設計優秀的演算法來優化執行的步驟,提高程式的效能。

第二種方式,是一種函式式的思維方式,在函式式的思維方式裡,結果比過程更重要,不需要關注執行的細節。程式的具體執行由編譯器來決定。這種情況下提高程式的效能是一個不容易的事情。

我們再回到Optional,來說說使用它容易陷入的誤區

使用isPresent&get來“優雅”的避免空值判斷

我們知道了Optional為我們提供了可以優雅的避免空指標的方案,我們以上的程式碼還可以這樣寫

Optional<User> user = Optional.ofNullable(getUser());

if (user.isPresent()) {
    return "000000";
}

if (Optional.ofNullable(user.get().getMobilePhone()).isPresent() 
        && !user.get().getMobilePhone().isEmpty()) {
    return user.get().getMobilePhone();
}

以上程式碼我們避免了像user==null這樣的程式碼,我們使用了Optional的isPresent方法進行優雅的判空,但是這不是最好的方案,也不是使用Optional的初衷,所以當你的程式碼裡出現了Optional的isPresent或者get方法你就要注意了,看下是否是正確的使用了Optional,另外在你使用get時,必須要使用isPresent做判斷

使用Optional型別做引數或者屬性

在設計類或者方法時,也沒有必要使用Optional作為引數和屬性,可以使用它作為返回值,這是推薦的。引數和屬性的本質是做資料的傳遞,Optional是作為一種避免使用null的容器類。

Lambda Expressions

Lambda表示式也是jdk8中新增的一大特性,也是JDK8中最火的一個特性,在上節中,我們已經使用了Lambda表示式。並且Lambda表示式也是函數語言程式設計思維的一種體現,上面也已經提到過,函數語言程式設計思維。

示例如下,有一個名字集合,我們來對名字進行排序、查詢能操作

List<String> names = Arrays.asList("Name1", "Name2", "S4", 
                                   "Name3", "Name4", "S1", "S2");

排序:使用()->{}替代匿名類

//jdk8 before
Collections.sort(names, new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
        if (o1.startsWith("N") && !o2.startsWith("N")) {
           return -1;
        }
        return 1;
    }
});

//Lambda
Collections.sort(names, (o1, o2) -> o1.startsWith("N") 
                 && !o2.startsWith("N") ? -1 : 1);

查詢:迴圈

//jdk8 before
List<String> startWithN = new ArrayList<>();
for (String name : names) {
    if (name.startsWith("N")) {
        startWithN.add(name);
    }
}

//Lambda
List<String> startWithN2 = names.stream()
        .filter((name) -> name.startsWith("N"))
        .collect(Collectors.toList());

在以上的示例中我們使用了stream,其中stream也是jdk8的一大特性這個將在下篇講。

函數語言程式設計

在java8中為了支援函式式的程式設計,java8引入了java.util.function的包,其中Predicate介面是支援Lambda的函式式的程式設計。

private static List<String> filter(List<User> users, 
                                   Function<User, String> function) {
    List<String> pros = new ArrayList<>();
    users.forEach((user) -> {
        pros.add(function.apply(user));
    });
    return pros;
}

List<User> users = new ArrayList<>();
users.add(new User("1232","2323"));
List<String> phones=filter(users,(user)->user.getPhone());
phones.forEach(System.out::println);

在以上的示例中,就實現了一個獲取一個user的集合中,所有的電話號碼的需求,當然以上的filter方法在stream中已經存在,直接使用即可,在上面我們只作為一個示例。

以上是直觀的感受了Lambda表示式的寫法的改變,下篇中,我們繼續探討Streams的用法以及介面的預設方法的支援。

歡迎關注我的公眾號MyArtNote

MyArtNote