一、Java8之前通路對象的屬性或方法
Java8之前通路對象的屬性或方法,會使用到以下方式,以下方式可能會造成空指針異常
String code = user.getAddress().getCountry().getCode().toUpperCase();
在上面的代碼中,如果要確定不發生空指針異常必須使用非空判斷,如下所示
if (null!=user) {
Address address = user.getAddress();
if (null!=address ) {
Country country = address.getCountry();
if (null!=country ) {
String code = country.getCode();
if (null!=code) {
code = code.toUpperCase();
}
}
}
}
代碼變得很冗長,難以維護。并且如果再新增加屬性,将會增加更多的if判斷。為了簡化這個過程,我們來看看用 Optional 類是怎麼做的。從建立和驗證執行個體,到使用其不同的方法,并與其它傳回相同類型的方法相結合,下面是見證 Optional 奇迹的時刻。
二、建立Optional對象
1. 建立包含空值的Optional
Optional.empty();
Optional.of()/Optional.ofNullable()
你可以使用Optional.of()或ofNullable()方法建立Optional對象,兩個方法的差別是,使用Optional.of()方法建立對象時,當參數為null時,調用get()方法,程式會抛出NullPointerException。如下圖所示

使用Optional.ofNullable(),當參數為null時,程式會抛出
java.util.NoSuchElementException: No value present
異常。如下圖所示
三、擷取Optional對象的值
1. get()方法
使用get()方法,此方法在值為null時,抛出異常
@Test
public void getOptionValue(){
String name = "John";
Optional<String> opt = Optional.ofNullable(name);
System.out.println(opt.get());
}
2. isPresent()
判斷是否有值,再進行後續操作
@Test
public void getOptionValue(){
String name = "John";
Optional<String> opt = Optional.ofNullable(name);
if(opt.isPresent()){
System.out.println(opt.get());
}
}
3. ifPresent()
判斷是否有值再進行後續操作,接受一個Consumer(消費者) 參數,如果對象不是空的,就對執行傳入的 Lambda 表達式
@Test
public void getOptionValue(){
String name = "John";
Optional<String> opt = Optional.ofNullable(name);
opt.ifPresent(System.out::println );
}
四、傳回預設值
1. orElse()
orElse()方法,它的工作方式非常直接,如果有值則傳回該值,否則傳回傳遞給它的參數值。如下示例代碼,如果name為null則傳回"Sim",如果name不為空則直接傳回name。
@Test
public void getOptionOrElse(){
String name = null;
Optional<String> opt = Optional.ofNullable(name);
String nameStr= opt.orElse("Sim");
System.out.println(nameStr);
}
2.orElseGet()
此方法略有不同。這個方法會在有值的時候傳回值,如果沒有值,它會執行作為參數傳入的 Supplier(供應者) 函數式接口,并将傳回其執行結果:
@Test
public void getOptionOrElseGet(){
User user =null;
User user2 = new User();
user2.setName("abc");
User user3= Optional.ofNullable(user).orElseGet(
() -> user2);
System.out.println(user3);
}
3.orElse和orElseGet差別
當Optional中對象為空是,兩者沒有差別都,傳回預設值或供給函數提供的值。
當Optional中對象不為空,orElse仍然執行産生預設值的方法,如下圖所示:
當Optional中對象不為空,orElseGet不會執行産生預設值的方法,如下圖所示:
五、傳回異常
1.orElseThrow()
當Optional對象值為空的時抛出異常,異常可以自定義。
@Test
public void getOptionOrElseThrow(){
User user2 = null;
//當user2為空時,抛出異常
User user3= Optional.ofNullable(user2).orElseThrow(
() -> new IllegalArgumentException() );
}
六、轉換值
1.map方法
map方法接收一個function接口,并且把傳回值包裝成Optional對象,使對傳回值進行鍊式調用的操作成為可能,如下代碼所示,調用map後,可緊着調用orElse
@Test
public void testMap(){
User user2 = new User();
user2.setName("Lisa");
user2.setAddress("上海");
String address =
Optional.ofNullable(user2).
map(u -> u.getAddress()).orElse("北京");
System.out.println(address);
}
2.flatMap
map方法接收一個function接口,傳回值直接是一個Optional對象,不需要進行重新包裝。
public Optional<String> getCode(){
return Optional.ofNullable(code);
}
@Test
public void testFlatMap(){
User user2 = new User();
user2.setName("Lisa");
user2.setAddress("上海");
String code =
Optional.ofNullable(user2).flatMap(u -> u.getCode()).orElse("北京");
System.out.println(code);
}
七、過濾值
Optional 類也提供了按條件“過濾”值的方法,filter() 接受一個 Predicate 參數,傳回測試結果為 true 的值。如果測試結果為 false,會傳回一個空的 Optional。
@Test
public void testFilter() {
User user = new User();
user.setEmail("[email protected]");
Optional<User> result =
Optional.ofNullable(user)
.filter(u -> StringUtils.isNotEmpty(u.getEmail()) && u.getEmail().contains("@"));
System.out.println(result);
}
八、怎麼使用Optional
1.使用場景
Optional 主要用作傳回類型。在擷取到這個類型的執行個體後,如果它有值,你可以取得這個值,否則可以進行一些替代行為。
Optional 類有一個非常有用的用例,就是将其與流或其它傳回 Optional 的方法結合,以建構流暢的API。
2.不适合使用場景
Optional 不是 Serializable。是以,它不應該用作類的字段。如果你需要序列化的對象包含 Optional 值,
Jackson 庫支援把 Optional 當作普通對象。也就是說,Jackson 會把空對象看作 null,而有值的對象則把其值看作對應域的值。這個功能在
jackson-modules-java8項目中。
它在另一種情況下也并不怎麼有用,就是在将其類型用作方法或建構方法的參數時,這樣做會讓代碼變得複雜,完全沒有必要。比如以下代碼
User user = new User(23, "Lisa", Optional.empty());
九、總結
Optional主要用來解決空指針異常,結合Stream流及Lambda表達式,使應用程式更加優化及健壯。