原文地址html
Java程序員面臨的最多見異常之一是NullPointerException。 JVM會在運行時將該異常做爲運行時異常拋出。java
衆所周知,當程序須要一個對象可是卻找到一個null
值時,就會拋出NullPointerException。對空指針異常進行處理是Java程序員最容易忽略的場景之一。程序員
在繼續執行常規業務邏輯以前,須要在應用程序中處理空指針異常,以免在運行時產生空指針異常。這致使了沒必要要的空指針檢測代碼的存在。數據庫
爲了在Java中處理這些用於進行空指針檢測的模板代碼,因此Java 8中引入了新類型Optional<T>
。api
據Oracle說,Java 8中引入的Optional做爲一個容器類型,用於包裝值不存在或爲null的狀況。 Optional是
java.util
包中一個具備final
修飾符的類。oracle
咱們來看看沒有Optional會遇到哪些問題。app
假設在咱們的程序中具備如下方法。此方法使用員工的ID
從數據庫中檢索員工的詳細信息:ide
Employee findEmployee(String id) {
...
};
複製代碼
假設在使用上述方法時所提供的ID
在數據庫中不存在。而後,該方法將返回null
值。如今,若是咱們已經編寫了下面的代碼:函數
Employee employee = findEmployee("1234");
System.out.println("Employee's Name = " + employee.getName());
複製代碼
上面的代碼將在運行時拋出NullPointerException異常,由於程序員在使用該值以前沒有對它進行空指針檢測。學習
如今,讓咱們看看Java 8引入的Optional類型將如何解決上述問題並幫助消除NullPointerException
異常。
如下是上述代碼修改事後的代碼:
Optional<Employee> findEmployee(String id) {
...
};
複製代碼
在上面的代碼中,咱們將返回類型修改成Optional<Employee>
來向調用者代表與給定ID
的對應的員工可能不存在。
如今,須要在調用方中明確表達這一事實。
調用者應該編寫以下代碼:
Optional <Employee> optional = findEmployee("1234");
optional.ifPresent(employee -> {
System.out.println("Employee name is " + employee.getName());
})
複製代碼
你能夠看到咱們在上述代碼的第一行中建立了一個Optional對象。如今,咱們可使用Optional對象上面的各類方法。
以上代碼段中的ifPresent()
方法僅在員工存在的狀況下才調用提供的lambda表達式。 若是員工不存在,則什麼事都不會作。
如下列出的是使用Optional的一些優勢:
Methods Description public static Optional empty() This method returns an empty Optional object. No value is present for this Optional. public static Optional of(T value) This method returns an Optional with the specified value that is not null. public static Optional ofNullable(T value) This method returns an Optional describing the specified value if the value is non-null; otherwise, it returns an empty Optional. public T get() If a value is present in this Optional, then it returns the value. Otherwise, it throws NoSuchElementException. public boolean isPresent() This method returns a true value if there is a value present. Otherwise, it returns false. public void ifPresent(Consumer<? super T> consumer) If a value is present, then the consumer with the provided value is invoked. Otherwise, it does nothing. public Optional filter(Predicate<? super T> predicate) If a value is present and it also matches the given predicate, then it returns an Optional describing the value. Otherwise, it returns an empty Optional. public Optional map(Function<? super T,? extends U> mapper) If a value is present, then the mapping function is applied, and if the result is not a null value, then it returns an Optional describing the result. Otherwise, it returns an empty Optional. public Optional flatMap(Function<? super T,Optional mapper) If the value is present, then it applies the provided Optional-bearing mapping function to it and it returns that result. Otherwise, it returns an empty Optional. public T orElse(T other) This method returns the value if present; otherwise, it returns other. public T orElseGet(Supplier<? extends T> other) This method returns the value if present. Otherwise, it invokes other and returns the result of the invocation. public T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X extends Throwable If the value is present, this method returns the contained value. Otherwise, it throws an exception to be created by the provided supplier. public boolean equals(Object obj) This method is used for indicating whether some other object is 「equal to」 this Optional or not. public int hashCode() This method returns the hash code value of the present value if it exists. Otherwise, it returns 0 (zero) . public String toString() This method is simply used to return a non-empty string representation of the Optional, which is suitable for debugging.
在本節中,咱們將研究用於建立Optional對象的方法:
1. 建立一個包含空值的Optional對象
下面的代碼演示瞭如何建立一個具備空值的Optional對象。它用來表示值爲null
的狀況。
Optional <Employee> employee = Optional.empty();
複製代碼
2. 建立一個具備非空值的Optional對象
下面的代碼展現瞭如何建立一個具備非空值的Optional對象。
Employee employee = new Employee("1234", "TechBlogStation");
Optional <Employee> optional = Optional.of(employee);
複製代碼
請注意,若是爲Optional.of()
的參數提供了null,則它將當即拋出NullPointerException異常,因此建立Optional對象將會失敗。
如今,讓咱們學習經過不一樣的方法檢查Optional對象中是否存在非空值:
1. isPresent()方法
若是Optional對象中存在非空值,則方法isPresent()
返回true,不然,它返回false。
if (optional.isPresent()) {
// optional中有值
System.out.println("Value - " + optional.get());
} else {
// optional中沒有值
System.out.println("Optional is empty");
}
複製代碼
2.ifPresent()方法
在方法ifPresent()
中,咱們將傳遞一個Customer函數過去。僅當Optional對象中存在非空值時,纔會執行該Consumer函數。
若是Optional對象中存放的是空值,則不執行任何操做:
optional.ifPresent(value -> {
System.out.println("Value present - " + value);
});
複製代碼
在上面的代碼中,咱們提供了lambda函數做爲ifPresent()
方法的參數。
Optional的get()
方法僅用於從Optional對象取出值。若是Optional對象中保存的是一個空值,則將引起NoSuchElementException異常。
Employee employee = optional.get()
複製代碼
在上述代碼中,若是optional對象中是個保存的是空值,則會拋出異常,所以建議在使用get()
方法以前,咱們應該首先檢查optional中是否有值。
若是Optional對象中保存的是空值,則返回調用orElse()
方法所傳入的參數做爲默認值。若是其中保存的是非空值,則返回其中保存的值。
請參見下面的示例:
// 使用三目表達式返回默認值
User finalEmployee = (employee != null) ? employee : new Employee("0", "Unknown Employee");
複製代碼
如今,使用Optional的orElse()
方法實現與上面相同的邏輯:
// 使用orElse()方法返回默認值
User finalEmployee = optional.orElse(new Employee("0", "Unknown Employee"));
複製代碼
咱們已經知道,若是Optional對象爲空,則方法orElse()
直接返回調用時傳入的參數做爲默認值,而orElseGet()
方法接受一個 Supplier做爲參數,而且當Optional對象中保存的是空值時調用Supplier。
Supplier返回的結果將成爲Optional的默認值。
User finalEmployee = optional.orElseGet(() -> {
return new Employee("0", "Unknown Employee");
});
複製代碼
若是Optional對象爲空,則可使用orElseThrow()
方法拋出一個異常。
它能夠用於REST API中指定的對象不存在的狀況。您可使用此方法拋出自定義異常,例如ResourceNotFound()
等:
@GetMapping("/employees/{id}")
public User getEmployee(@PathVariable("id") String id) {
return employeeRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Employee not found with id " + id););
}
複製代碼
假設您有一個Employee的Optional對象。如今,你正在檢查員工的性別並相應地調用一個函數。
下面是這樣作的舊方法:
if(employee != null && employee.getGender().equalsIgnoreCase("MALE")) {
// 調用函數
}
複製代碼
如今,讓咱們看看如何使用Optional的filter()
方法來達到此目的:
optional.filter(user -> employee.getGender().equalsIgnoreCase("MALE")) .ifPresent(() -> {
// 你的函數
})
複製代碼
filter()
方法將一個謂詞函數做爲參數。若是Optional對象中包含一個非空值而且該值與提供的謂詞匹配,則此方法將返回一個包含該值的Optional對象。
不然,此方法返回一個包含空值的Optional對象。
假設咱們有一個場景,咱們要提取僱員的地址,還要根據指定條件打印出該地址。
考慮如下示例:
咱們在Employee
類中有getAddress()
方法:
Address getAddress() {
return this.address;
}
複製代碼
如下是實現上述需求的典型方式:
if (employee != null) {
Address address = employee.getAddress();
if (address != null && address.getCountry().equalsIgnoreCase("USA")) {
System.out.println("Employee belongs to USA");
}
}
複製代碼
如今看看如何使用Optional的map()
方法實現同樣的功能:
userOptional.map(Employee::getAddress).filter(address -> address.getCountry().equalsIgnoreCase("USA")).ifPresent(() -> {
System.out.println("Employee belongs to USA");
});
複製代碼
與之前的方法相比,以上代碼可讀性強、簡潔與高效。
讓咱們更詳細地分析一下代碼:
// 使用map()方法提取地址
Optional<Address> addressOptional = employeeOptional.map(Employee::getAddress)
// 過濾出來自USA的員工
Optional<Address> usaAddressOptional = addressOptional.filter(address -> address.getCountry().equalsIgnoreCase("USA"));
// 若是員工來自USA則輸出一些信息
usaAddressOptional.ifPresent(() -> {
System.out.println("Employee belongs to USA");
});
複製代碼
在上述代碼段中,出現如下任意一種狀況,方法map()
返回一個包含空值的Optional對象:
employeeOptional
中不存在員工。getAddress()
返回null。不然,返回包含員工地址的Optional<Address>
對象。
如今,讓咱們再次考慮上述與map()
方法相關的示例。
你能夠看到即便Employee地址能夠爲null,咱們也沒有使用Optional<Address>做爲getAddress()
方法的返回類型。
若是getAddres()
方法的返回類型爲Optional<Address>,則下面這一行代碼將會出現問題:
Optional<Address> addressOptional = employeeOptional.map(Employee::getAddress)
複製代碼
因爲getAddress()
方法返回Optional <Address>,所以employeeOptional.map()
的返回類型是Optional<Optional<Address>>
。
下面是對於這種狀況的演示:
Optional<Optional<Address>> addressOptional = employeeOptional.map(Employee::getAddress)
複製代碼
這種狀況下,咱們嵌套了Optional,可是咱們不但願這樣作,因此咱們如今可使用flatMap()
方法解決這個問題:
Optional<Address> addressOptional = employeeOptional.flatMap(Employee::getAddress)
複製代碼
請注意,若是您的傳遞給
map()
方法的函數返回一個Optional對象,則應該使用flatMap()
方法替換掉map()
方法,從而獲取到展開後的結果。
咱們已經學習了Java 8中的Optional是什麼,它的優勢以及在Java中經過使用Optional解決問題。此外,經過一些示例,咱們可以更好地明白Java 8中Optional類的一些方法。
謝謝閱讀!
Java 8 Optional Uses and Best Practices