Optional
在Java中對一個空對象進行操作時,便會拋出最常見的異常NullPointerException。為了改善這個問題,Java8中提供了一個java.util.Optional<T>類型。
Optional類的Javadoc描述如下:這是一個可以為null的容器對象。如果值存在isPresent()方法會返回true,調用get()方法會返回該對象。
1. 示例1:
下面介紹Optional類的使用方法。 假如有一個像下面這樣的類層次結構:
@Data
public class Department {
private Employee employee;
public Department(Employee employee) {
this.employee = employee;
}
}
@Data
class Employee{
private Girl girlFriend;
public Employee(Girl girlFriend) {
this.girlFriend = girlFriend;
}
}
@Data
class Girl{
private String name;
public Girl(String name) {
this.name = name;
}
}
部門Department類包含一個員工employee屬性,類型為Employee,員工Employee類包含girlFriend屬性,類型為Girl。假如現(xiàn)在要獲取部門某個員工的女朋友,我們通常是這樣獲取的:
public String getGirlFriend(Department department){
if(department !=null){
Employee employee = department.getEmployee();
if(employee !=null){
Girl girl = employee.getGirlFriend();
if(girl!=null){
return girl.getName();
}
return "單身狗";
}
return "沒有員工";
}
return "部門為空";
}
可以看到,在每次引用變量的屬性時,都要先判斷變量是否為空,如果不做該檢查將可能導致NullPointerException。下面我們將使用Optional來改善這種層層嵌套,啰嗦的代碼。
public String getGirlFriend2(Department department){
Optional<Department> opt = Optional.ofNullable(department);
return opt.map(Department::getEmployee)
.map(Employee::getGirlFriend)
.map(Girl::getName)
.orElseThrow(NoSuchFieldError::new);
// 如果發(fā)生NPE異常,則返回默認String "default"
String girlName = opt.map(Department::getEmployee)
.map(Employee::getGirl)
.map(Girl::getName)
.orElse("default");
}
2. 示例2:
public class MainDemo {
public static void main(String[] args) {
Integer value1 = null;
Integer value2 = new Integer(10);
// Optional.ofNullable - 允許傳遞為null參數(shù)
Optional<Integer> a = Optional.ofNullable(value1);
// Optional.of - 如果傳遞的參數(shù)是null,拋出異常NullPointException
Optional<Integer> b = Optional.of(value2);
System.out.println(sum(a, b));
}
private static Integer sum(Optional<Integer> a, Optional<Integer> b) {
// Optional.isPresent - 判斷值是否存在
System.out.println("第一個參數(shù)值存在:" + a.isPresent());
System.out.println("第二個參數(shù)值存在:" + b.isPresent());
// Optional.orElse - 如果值存在,返回它,否則返回默認值
Integer value1 = a.orElse(new Integer(0));
Integer value2 = b.get();
return value1 + value2;
}
}
輸出:
第一個參數(shù)值存在:false
第二個參數(shù)值存在:true
10
下面我們來具體學習下Optional:
創(chuàng)建Optional
創(chuàng)建一個Optional對象有好幾種方式:
- 創(chuàng)建一個空的Optional
我們可以使用靜態(tài)工廠方法Optional.empty,創(chuàng)建一個空的Optional對象:
Optional<Department> department = Optional.empty();
- 根據(jù)非空值創(chuàng)建Optional
我們也可以使用靜態(tài)工廠方法Optional.of來創(chuàng)建一個非空對象的Optional對象:
Optional<Employee> optEmployee = Optional.of(employee);
如果employee為空,這段代碼會立即拋出一個NullPointerException。
- 創(chuàng)建可以為null的Optional
使用靜態(tài)工廠方法Optional.ofNullable,我們可以創(chuàng)建一個允許null值的Optional對象:
Optional<Employee> optEmployee = Optional.ofNullable(employee);
如果employee為空,對其調用get方法將拋出NoSuchElementException。
Optional方法
Optional類包含了許多方法,下面介紹這些方法的使用。
- isPresent
顧名思義,如果值存在返回true,否則返回false。如:
Optional<Department> opt = Optional.ofNullable(department);
if(opt.isPresent()){
System.out.println(opt.get().getEmployee());
}
- get
如果Optional有值則將其返回,否則拋出NoSuchElementException。下面舉個拋出NoSuchElementException的例子:
try {
Optional.empty().get();
} catch (Exception e) {
e.printStackTrace();
}
代碼將捕獲到 java.util.NoSuchElementException: No value present 異常。
- ifPresent
如果Optional實例有值則為其調用Consumer(函數(shù)描述符為T -> void),否則不做處理。如:
girl.ifPresent(g -> System.out.println("我有女朋友,名字是:" + g.getName()));
- orElse
如果Optional實例有值則將其返回,否則返回orElse方法傳入的參數(shù)。如:
System.out.println(Optional.empty().orElse("There is no value present!"));
程序將輸出There is no value present!。
- orElseGet
orElseGet與orElse方法類似,orElse方法將傳入的字符串作為默認值,而orElseGet方法可以接受Supplier(函數(shù)描述符為() -> T)來生成默認值。如:
System.out.println(Optional.empty().orElseGet(() -> "There is no value present!"));
程序同樣輸出There is no value present!。
- orElseThrow
如果有值則將其返回,否則拋出Supplier接口創(chuàng)建的異常。如:
try {
Optional.empty().orElseThrow(NoSuchElementException::new);
} catch (Exception e) {
e.printStackTrace();
}
代碼將捕獲到 java.util.NoSuchElementException: No value present 異常。
7.map
如果Optional有值,則對其執(zhí)行調用Function函數(shù)描述符為(T -> R)得到返回值。如果返回值不為null,則創(chuàng)建包含F(xiàn)unction回值的Optional作為map方法返回值,否則返回空Optional。
Optional<String> upperName = name.map(String::toUpperCase);
System.out.println(upperName.orElse("No value found"));
- flatMap
如果有值,為其執(zhí)行Function函數(shù)返回Optional類型返回值,否則返回空Optional。flatMap與map方法類似,區(qū)別在于flatMap中的Function函數(shù)返回值必須是Optional。調用結束時,flatMap不會對結果用Optional封裝。如:
upperName = name.flatMap((value) -> Optional.of(value.toUpperCase()));
System.out.println(upperName.orElse("No value found"));
- filter
filter個方法通過傳入Predicate(函數(shù)描述符為T -> Boolean)對Optional實例的值進行過濾。如:
Optional<String> name = Optional.of("Jane");
Optional<String> LongName = name.filter((value) -> value.length() >= 3);
System.out.println(LongName.orElse("名字長度小于3個字符"));
方法輸出Jane。