本篇文章將詳細介紹 Optional 類,以及如何用它消除代碼中的 null 檢查。在開始以前首先來看下什麼是 NPE,以及在 Java 8 以前是如何處理 NPE 問題的。java
空指針異常(NullPointException,簡稱 NPE)能夠說是全部 Java 程序員都遇到過的一個異常,雖然 Java 從設計之初就力圖讓程序員脫離指針的苦海,可是指針確實是實際存在的,而 Java 設計者也只能是讓指針在 Java 語言中變得更加簡單易用,而不能徹底剔除,因此纔有了常見對的關鍵字 null。程序員
空指針異常是一個運行時異常,對於這一類異常,若是沒有明確的處理方式,那麼最佳實踐在於讓程序早點掛掉。當異常真的發生的時候,處理方式也很簡單,在存在異常的地方添加一個 if 語句斷定便可。好比下面的代碼:編程
public String bindUserToRole(User user) { if (user == null) { return; } String roleId = user.getRoleId(); if (roleId == null) { return; } Role = roleDao.findOne(roleId); if (role != null) { role.setUserId(user.getUserId()); roleDao.save(role); } }
可是這樣的應對方式會讓程序出現愈來愈多的 null 斷定,一個良好的程序設計,應該讓代碼中儘可能少出現 null 關鍵字,所以 Java 8 引入 Optional 類來避免 NPE 問題,同時也提高了代碼的美觀度。但並非對 null 關鍵字的一種替代,而是對於 null 斷定提供了一種更加優雅的實現,從而避免 NPE 問題。函數式編程
爲了更好的解決和避免常見的 NPE 問題,Java 8 中引入了一個新的類 java.util.Optional<T>,Optional 值能夠爲 null,若是值存在,調用 isPresent() 方法返回 true,調用 get() 方法能夠獲取值。函數
Optional 類提供類三個方法用於實例化一個 Optional 對象,它們分別爲 empty()、of()、ofNullable(),這三個方法都是靜態方法,能夠直接調用。spa
empty() 方法用於建立一個沒有值的Optional對象:設計
Optional<String> emptyOpt = Optional.empty();
empty() 方法建立的對象沒有值,若是對 emptyOpt 變量調用 isPresent() 方法會返回 false,調用 get() 方法拋出 NPE 異常。指針
of() 方法使用一個非空的值建立Optional對象:code
String str = "Hello World"; Optional<String> notNullOpt = Optional.of(str);
ofNullable() 方法接收一個能夠爲null的值:對象
Optional<String> nullableOpt = Optional.ofNullable(str);
若是 str 的值爲 null,獲得的 nullableOpt 是一個沒有值的 Optional 對象。
若是咱們要獲取 User 對象中的 roleId 屬性值,常見的方式是直接獲取:
String roleId = null; if (user != null) { roleId = user.getRoleId(); }
使用 Optional 中提供的 map() 方法能夠更簡單地實現:
Optional<User> userOpt = Optional.ofNullable(user); Optional<String> roleIdOpt = userOpt.map(User::getRoleId);
使用 orElse()方法獲取值
Optional 類還包含其餘方法用於獲取值,這些方法分別爲:
下面來看看這三個方法的具體用法:
String str = "Hello World"; Optional<String> strOpt = Optional.of(str); String orElseResult = strOpt.orElse("Hello BeiJing"); String orElseGet = strOpt.orElseGet(() -> "Hello BeiJing"); String orElseThrow = strOpt.orElseThrow( () -> new IllegalArgumentException("Argument 'str' cannot be null or blank."));
此外,Optional 類還提供了一個 ifPresent() 方法,該方法接收一個 Consumer<? super T> 函數式接口,通常用於將信息打印到控制檯:
Optional<String> strOpt = Optional.of("Hello World"); strOpt.ifPresent(System.out::println);
使用 filter() 方法過濾
filter() 方法可用於判斷 Optional 對象是否知足給定條件,通常用於條件過濾:
Optional<String> optional = Optional.of("wupx94@qq.com"); optional = optional.filter(str -> str.contains("wupx"));
在上面的代碼中,若是 filter() 方法中的 Lambda 表達式成立,filter() 方法會返回當前 Optional 對象值,不然,返回一個值爲空的 Optional 對象。
關於 Optional 使用建議:
- 儘可能避免在程序中直接調用 Optional 對象的 get() 和 isPresent() 方法
- 避免使用 Optional 類型聲明實體類的屬性
上面提到建立 Optional 對象有三個方法,empty() 方法比較簡單,主要是 of() 和 ofNullable() 方法。當你肯定一個對象不可能爲 null 的時候,應該使用 of() 方法,不然,儘量使用 ofNullable() 方法,好比:
public static void method(Role role) { // 當Optional的值經過常量得到或者經過關鍵字 new 初始化,能夠直接使用 of() 方法 Optional<String> strOpt = Optional.of("Hello World"); Optional<User> userOpt = Optional.of(new User()); // 方法參數中role值不肯定是否爲null,使用 ofNullable() 方法建立 Optional<Role> roleOpt = Optional.ofNullable(role); }
orElse() 方法的使用
return str != null ? str : "Hello World"
上面的代碼表示判斷字符串 str 是否爲空,不爲空就返回,不然,返回一個常量。使用 Optional 類能夠表示爲:
return strOpt.orElse("Hello World")
簡化 if-else
User user = ... if (user != null) { String userName = user.getUserName(); if (userName != null) { return userName.toUpperCase(); } else { return null; } } else { return null; }
上面的代碼能夠簡化成:
User user = ... Optional<User> userOpt = Optional.ofNullable(user); return userOpt.map(User::getUserName) .map(String::toUpperCase) .orElse(null);
Optional 是一個 final 類,未實現任何接口,Optional 不能序列化,不能做爲類的字段(field),因此當咱們在利用該類包裝定義類的屬性的時候,若是咱們定義的類有序列化的需求,那麼由於 Optional 沒有實現 Serializable 接口,這個時候執行序列化操做就會有問題:
import java.util.Optional; import lombok.Data; @Data public class User implements Serializable { private String name; private String gender; private Optional<String> phone; // 不能序列化 }
能夠經過本身實現 getter 方法,使 Lomok 不自動生成,以下:
import java.util.Optional; import lombok.Data @Data public class User implements Serializable { private String name; private String gender; private String phone; public Optional<String> getPhone() { return Optional.ofNullable(phone); } }
Java 8 中 Optional 類可讓咱們以函數式編程的方式處理 null 值,拋棄了 Java 8 以前須要嵌套大量 if-else 代碼塊,使代碼可讀性有了很大的提升,可是應儘可能避免使用 Optional 類型聲明實體類的屬性。