你真的開始用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的用法以及介面的預設方法的支援。