Java 8 Optional的正確姿勢(轉)


Optional是Java8提供的為了解決null安全問題的一個API。善用Optional可以使我們代碼中很多繁瑣、丑陋的設計變得十分優雅。

當我們還在以如下幾種方式使用 Optional 時, 就得開始檢視自己了

調用 isPresent() 方法時
調用 get() 方法時
Optional 類型作為類/實例屬性時
Optional 類型作為方法參數時
isPresent() 與 obj != null 無任何區別, 我們的生活依然在步步驚心. 而沒有 isPresent() 作鋪墊的 get() 調用在 IntelliJ IDEA 中會收到告警。調用 Optional.get() 前不事先用 isPresent() 檢查值是否可用. 假如 Optional 不包含一個值, get() 將會拋出一個異常!
把 Optional 類型用作屬性或是方法參數在 IntelliJ IDEA 中更是強力不推薦的!
使用任何像 Optional 的類型作為字段或方法參數都是不可取的. Optional 只設計為類庫方法的, 可明確表示可能無值情況下的返回類型. Optional 類型不可被序列化, 用作字段類型會出問題的!!!
正確的應該是:

盡量避免使用的地方:
1、避免使用Optional.isPresent()來檢查實例是否存在,因為這種方式和null != obj沒有區別,這樣用就沒什么意義了。
2、避免使用Optional.get()方式來獲取實例對象,因為使用前需要使用Optional.isPresent()來檢查實例是否存在,否則會出現NPE問題。
3、避免使用Optional作為類或者實例的屬性,而應該在返回值中用來包裝返回實例對象。
4、避免使用Optional作為方法的參數,原因同3。

 

下面舉例幾個, 作為最簡單的示例:

//不過,千萬不要改寫成這副樣子。
public static String getName(User u) {
if (u == null || u.name == null)
return "Unknown";
return u.name;
}


public static String getName(User u) {
Optional<User> user = Optional.ofNullable(u);
if (!user.isPresent())
return "Unknown";
return user.get().name;
}
//這樣改寫非但不簡潔,而且其操作還是和第一段代碼一樣。無非就是用isPresent方法來替代u==null。這樣的改寫並不是Optional正確的用法,我們再來改寫一次。

public static String getName(User u) {
return Optional.ofNullable(u)
.map(user->user.name)
.orElse("Unknown");
}

//這樣才是正確使用Optional的姿勢。那么按照這種思路,我們可以安心的進行鏈式調用,而不是一層層判斷了
以上是別人的例子, 下面是我寫的例子:

//對參數進行校驗
public static void main(String[] args) {
String id=null;
Optional.ofNullable(id).filter(StringUtils::isNoneBlank)
.orElseThrow(()->new RuntimeException("id不能為空"));

}
 

對象中參數的的校驗.

package com;

import org.apache.commons.lang3.StringUtils;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

/**
* @author zk
* @Description:
* @date 2019-11-20 15:49
*/
public class Demo {


public static void main(String[] args) {

User user = new User(18, "程程");

Optional<User> optional = Optional.ofNullable(user);
//校驗id 和name 是否為null
optional.map(user1 -> user1.getId()).orElseThrow(() -> new RuntimeException("id不能為空"));
optional.filter(user1 -> StringUtils.isNoneBlank(user1.getName())).orElseThrow(() -> new RuntimeException("名字不能為空"));

User user2 = new User();
Optional<User> optionalUser = Optional.ofNullable(user2);
//參數校驗
//optionalUser.map(u->u.getId()).orElseThrow(()->new RuntimeException("id不能為空"));
//optionalUser.filter(u->StringUtils.isNotBlank(u.getName())).orElseThrow(()->new RuntimeException("名字不能為空"));

//獲取默認值
String anElse = optionalUser.map(u -> u.getName()).orElse("默認名字");
System.out.println(anElse);

//為空就獲取到....
optionalUser.orElseGet(() -> new User(18, "程程"));

//存在則做什么處理
optionalUser.ifPresent(u -> u.setName(u.getName() + "123"));

//不存在返回一個默認值
optionalUser.map(u -> u.getMoneyList()).orElse(Collections.emptyList());
/**
//上面避免了我們類似 Java 8 之前的做法
if(optionalUser.isPresent()) {
return optionalUser.get().getOrders();
} else {
return Collections.emptyList();
}
*/

//對當前這個名字進行處理
optionalUser.map(u -> u.getName()).map(name -> name.toUpperCase()).orElse(null);
/**
User user = .....
if(user != null) {
String name = user.getUsername();
if(name != null) {
return name.toUpperCase();
} else {
return null;
}
} else {
return null;
}
*/



//三個參數的校驗
User user3 = new User(null, "程程", "大");
Optional.ofNullable(user3).filter(u -> StringUtils.isNotBlank(u.getName()))
.filter(u -> StringUtils.isNotBlank(u.getLocation()))
.map(u -> u.getId()).orElseThrow(() -> new RuntimeException("user中屬性不能為空"));


}


static class User {
private Integer id;
private String name;
private String location;
private List<String> moneyList;

public List<String> getMoneyList() {
return moneyList;
}

public void setMoneyList(List<String> moneyList) {
this.moneyList = moneyList;
}

public String getLocation() {
return location;
}

public void setLocation(String location) {
this.location = location;
}

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public User(Integer id, String name) {
this.id = id;
this.name = name;
}

public User() {
}

public User(Integer id, String name, String location) {
this.id = id;
this.name = name;
this.location = location;
}
}


}
 

一下內容來自其他文章了.希望對你們有一些啟發,

map 函數隆重登場
當 user.isPresent() 為真, 獲得它關聯的 orders的映射集合, 為假則返回一個空集合時, 我們用上面的 orElse, orElseGet 方法都乏力時, 那原本就是 map 函數的責任, 我們可以這樣一行

return user.map(u -> u.getOrders()).orElse(Collections.emptyList())
 
//上面避免了我們類似 Java 8 之前的做法
if(user.isPresent()) {
  return user.get().getOrders();
} else {
  return Collections.emptyList();
}

map 是可能無限級聯的, 比如再深一層, 獲得用戶名的大寫形式

return user.map(u -> u.getUsername())
           .map(name -> name.toUpperCase())
           .orElse(null);
//這要擱在以前, 每一級調用的展開都需要放一個 null 值的判斷

User user = .....
if(user != null) {
  String name = user.getUsername();
  if(name != null) {
    return name.toUpperCase();
  } else {
    return null;
  }
} else {
  return null;
}

filter() :如果有值並且滿足條件返回包含該值的Optional,否則返回空Optional。

Optional<String> longName = name.filter((value) -> value.length() > 6);  
System.out.println(longName.orElse("The name is less than 6 characters"));//輸出Sanaulla  

flatMap() :
如果有值,為其執行mapping函數返回Optional類型返回值,否則返回空Optional。flatMap與map(Funtion)方法類似,區別在於flatMap中的mapper返回值必須是Optional。調用結束時,flatMap不會對結果用Optional封裝。
flatMap方法與map方法類似,區別在於mapping函數的返回值不同。map方法的mapping函數返回值可以是任何類型T,而flatMap方法的mapping函數必須是Optional。

upperName = name.flatMap((value) -> Optional.of(value.toUpperCase()));  
System.out.println(upperName.orElse("No value found"));//輸出SANAULLA
————————————————
版權聲明:本文為CSDN博主「長河」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/u010398771/article/details/103165917


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM