Optional類的正確使用

本帖最後由 小刀葛小倫 於 2019-8-22 17:08 編輯

Java8新特性:Optional類的正確使用姿勢
空指針異常是我們在實際開發中經常會遇到的問題,爲了防止程序因爲異常而中斷,通常要在代碼中添加大量的非空驗證,例如一個釋放 JDBC 相關資源的代碼,如下所示。
[Java] 純文本查看 複製代碼
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
public static void release(Connection conn,Statement stmt,ResultSet rs) {
try {
if(conn != null) {
conn.close();
}
if(stmt != null) {
stmt.close();
}
if(rs != null) {
rs.close();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
 
上述代碼中的非空驗證並沒有多少技術含量,同時會使代碼顯得臃腫不堪,爲了解決這類問題,Java8 引入了 Optional 類,Optional 就是一個容器,它可以保存任意數據類型的值,包括 null,開發者無需進行空值檢測,Optional 類的引入很好的解決了實際開發中的空指針異常問題,接下來我們來詳細學習 Optional 類的使用。
Optional 常用方法
1、empty(),返回一個空的 Optional 對象,定義如下所示。
[Java] 純文本查看 複製代碼
1
2
3
4
5
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
 
具體操作如下所示。
[Java] 純文本查看 複製代碼
1
2
3
4
5
6
public class Test {
public static void main(String[] args) {
Optional optional = Optional.empty();
System.out.println(optional);
}
}


 
2、of(T value),根據傳入的 value 創建一個 Optional 對象,定義如下所示。
[Java] 純文本查看 複製代碼
1
2
3
public static <T> Optional<T> of(T value) {
return new Optional<>(value);
}
 
具體操作如下所示。
[Java] 純文本查看 複製代碼
1
2
3
4
5
6
public class Test {
public static void main(String[] args) {
Optional optional = Optional.of("Hello World");
System.out.println(optional);
}
}


 
可以看到輸出的結果是 Optional[Hello World],如果要取出 Hello World 直接調用 get 方法即可,如下所示。
[Java] 純文本查看 複製代碼
1
2
3
4
5
6
public class Test {
public static void main(String[] args) {
Optional optional = Optional.of("Hello World");
System.out.println(optional.get());
}
}


 
但是需要注意的是,of 方法不能傳 null,否則會拋出空指針異常,如下所示。
[Java] 純文本查看 複製代碼
1
2
3
4
5
6
public class Test {
public static void main(String[] args) {
Optional optional = Optional.of(null);
System.out.println(optional);
}
}



 
3、ofNullable(T value),和 of(T value) 方法類似,都是用來創建 Optional 對象的,區別在於 ofNullable(T value) 方法可以傳 null,定義如下所示。
[Java] 純文本查看 複製代碼
1
2
3
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
 
具體操作如下所示。
[Java] 純文本查看 複製代碼
1
2
3
4
5
6
public class Test {
public static void main(String[] args) {
Optional optional = Optional.ofNullable(null);
System.out.println(optional);
}
}
 
4、get(),返回 Optional 中存儲的任意類型值,如果 Optional 中的值爲 null,則拋出 java.util.NoSuchElementException,定義如下所示。
[Java] 純文本查看 複製代碼
1
2
3
4
5
6
public T get() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
 
具體操作如下所示。
[Java] 純文本查看 複製代碼
01
02
03
04
05
06
07
08
09
10
11
public class Test {
public static void main(String[] args) {
Optional optional = Optional.ofNullable("Hello World");
System.out.println(optional.get());
optional = Optional.ofNullable(100);
System.out.println(optional.get());
Integer[] array = {1,2,3,4,5,6};
optional = Optional.ofNullable(array);
System.out.println(optional.get());
}
}


 
如果是下面這種情況,直接拋出 java.util.NoSuchElementException 異常。
[Java] 純文本查看 複製代碼
1
2
3
4
5
6
public class Test {
public static void main(String[] args) {
Optional optional = Optional.ofNullable(null);
System.out.println(optional.get());
}
}


 
5、isPresent(),判斷 Optional 存儲的值是否存在,返回 true 表示有值,false 表示值不存在,定義如下所示。
[Java] 純文本查看 複製代碼
1
2
3
public boolean isPresent() {
return value != null;
}
 
具體使用如下所示。
[Java] 純文本查看 複製代碼
1
2
3
4
5
6
7
8
public class Test {
public static void main(String[] args) {
Optional optional = Optional.ofNullable("Hello World");
System.out.println(optional.isPresent());
optional = Optional.ofNullable(null);
System.out.println(optional.isPresent());
}
}


 
6、ifPresent(Consumer<? super T> consumer),如果值存在,執行 Consumer 的具體操作,如果值不存在,不做任何操作,定義如下所示。
[Java] 純文本查看 複製代碼
1
2
3
4
5
public void ifPresent(Consumer<? super T> action) {
if (value != null) {
action.accept(value);
}
}
 
具體操作如下所示。
[Java] 純文本查看 複製代碼
01
02
03
04
05
06
07
08
09
10
11
12
13
public class Test {
public static void main(String[] args) {
//值存在
List<Integer> list = Arrays.asList(1,2,3,4,5,6);
Optional optional = Optional.ofNullable(list);
System.out.print("值存在:");
optional.ifPresent(System.out::println);
//值不存在
optional = Optional.ofNullable(null);
System.out.print("值不存在:");
optional.ifPresent((str)-> System.out.println(str));
}
}


 
7、ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction),如果值存在,執行 Consumer 的具體操作,如果值不存在,執行 emptyAction 的具體操作,定義如下所示。
[Java] 純文本查看 複製代碼
1
2
3
4
5
6
7
public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) {
if (value != null) {
action.accept(value);
} else {
emptyAction.run();
}
}
 
具體操作如下所示。
[Java] 純文本查看 複製代碼
01
02
03
04
05
06
07
08
09
10
11
12
13
public class Test {
public static void main(String[] args) {
//值存在
List<Integer> list = Arrays.asList(1,2,3,4,5,6);
Optional optional = Optional.ofNullable(list);
System.out.print("值存在:");
optional.ifPresentOrElse(System.out::println,()-> System.out.println("value is null"));
//值不存在
optional = Optional.ofNullable(null);
System.out.print("值不存在:");
optional.ifPresentOrElse(System.out::println,()-> System.out.println("value is null"));
}
}



 
8、filter(Predicate<? super T> predicate),根據傳入的 Predicate 對 Optional 中的值進行過濾,滿足條件則返回該 Optional 對象,否則返回一個空的 Optional,定義如下所示。
[Java] 純文本查看 複製代碼
1
2
3
4
5
6
7
8
public Optional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent()) {
return this;
} else {
return predicate.test(value) ? this : empty();
}
}
 
具體操作如下所示。
[Java] 純文本查看 複製代碼
1
2
3
4
5
6
7
8
9
public class Test {
public static void main(String[] args) {
Optional optional = Optional.ofNullable(1);
Predicate<Integer> predicate = num -> num >= 3;
System.out.println(optional.filter(predicate));
predicate = num -> num < 3;
System.out.println(optional.filter(predicate));
}
}


 
9、map(Function<? super T, ? extends U> mapper),如果 Optional 有值,則執行 mapper 映射函數,並獲取其返回值,如果返回值不爲 null,則返回一個包含返回值的 Optional 對象,否則返回一個空的 Optional 對象,定義如下所示。
[Java] 純文本查看 複製代碼
1
2
3
4
5
6
7
8
public <U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent()) {
return empty();
} else {
return Optional.ofNullable(mapper.apply(value));
}
}
 
具體操作如下所示。
[Java] 純文本查看 複製代碼
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
public class Test {
public static void main(String[] args) {
ClassB classB = new ClassB();
ClassA classA = new ClassA();
classA.setClassB(classB);
Optional optional = Optional.ofNullable(classA)
.map((classA1)->classA1.getClassB());
System.out.println(optional);
}
}
class ClassA {
private ClassB classB;
public ClassB getClassB() {
return classB;
}
public void setClassB(ClassB classB) {
this.classB = classB;
}
}
class ClassB {
}
 
上述代碼表示如果 classA 中包含 classB,則直接返回一個 Optional[ClassB] 對象,否則返回一個空的 Optional 對象,
 
對上述代碼進行修改,刪除 classA.setClassB(classB); 如下所示。
[Java] 純文本查看 複製代碼
1
2
3
4
5
ClassB classB = new ClassB();
ClassA classA = new ClassA();
Optional optional = Optional.ofNullable(classA)
.map((classA1)->classA1.getClassB());
System.out.println(optional);



 
這裏需要注意的是 map 方法的調用必須和 Optional 的創建連起來寫,如下所示。
[Java] 純文本查看 複製代碼
1
Optional optional = Optional.ofNullable(classA).map((classA1)->classA1.getClassB());
 
不能分開寫,錯誤寫法如下所示。
[Java] 純文本查看 複製代碼
1
2
Optional optional = Optional.ofNullable(classA);
optional.map((classA1)->classA1.getClassB());
 
10、flatMap(Function<? super T, ? extends Optional<? extends U>> mapper),功能與 map 類似,區別在於 map 的 mapper 映射函數可返回任意數據類型,但是 flatMap 的 mapper 映射函數只能返回 Optional 類型,定義如下所示。
[Java] 純文本查看 複製代碼
01
02
03
04
05
06
07
08
09
10
public <U> Optional<U> flatMap(Function<? super T, ? extends Optional<? extends U>> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent()) {
return empty();
} else {
@SuppressWarnings("unchecked")
Optional<U> r = (Optional<U>) mapper.apply(value);
return Objects.requireNonNull(r);
}
}
 
具體操作如下所示。
[Java] 純文本查看 複製代碼
01
02
03
04
05
06
07
08
09
10
11
12
public class Test {
public static void main(String[] args) {
ClassB classB = new ClassB();
ClassA classA = new ClassA();
classA.setClassB(classB);
Optional optional = Optional.ofNullable(classA)
.flatMap((classA1)->{
return Optional.ofNullable(classA1.getClassB());
});
System.out.println(optional);
}
}



 
11、orElse(T other),如果 Optional 的值存在則返回,否則返回 other,定義如下所示。
[Java] 純文本查看 複製代碼
1
2
3
public T orElse(T other) {
return value != null ? value : other;
}
 
具體操作如下所示。
[Java] 純文本查看 複製代碼
1
2
3
4
5
6
7
8
public class Test {
public static void main(String[] args) {
Optional optional = Optional.ofNullable(1);
System.out.println(optional.orElse("value is null"));
optional = Optional.ofNullable(null);
System.out.println(optional.orElse("value is null"));
}
}



 
12、orElseGet(Supplier<? extends T> supplier),功能與 orElse 類似,區別在於 orElse 可直接返回某個值,orElseGet 需要執行 supplier,並返回其結果,多了一個步驟,定義如下所示。
[Java] 純文本查看 複製代碼
1
2
3
public T orElseGet(Supplier<? extends T> supplier) {
return value != null ? value : supplier.get();
}
 
具體操作如下所示。
[Java] 純文本查看 複製代碼
1
2
3
4
5
6
7
8
public class Test {
public static void main(String[] args) {
Optional optional = Optional.ofNullable(1);
System.out.println(optional.orElseGet(()->"value is null"));
optional = Optional.ofNullable(null);
System.out.println(optional.orElseGet(()->"value is null"));
}
}



 
13、orElseThrow(),如果值存在則返回,否則拋出 NoSuchElementException,定義如下所示。
[Java] 純文本查看 複製代碼
1
2
3
4
5
6
public T orElseThrow() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
 
具體操作如下所示。
[Java] 純文本查看 複製代碼
1
2
3
4
5
6
7
8
public class Test {
public static void main(String[] args) {
Optional optional = Optional.ofNullable(1);
System.out.println(optional.orElseThrow());
optional = Optional.ofNullable(null);
System.out.println(optional.orElseThrow());
}
}


 
14、orElseThrow(Supplier<? extends X> exceptionSupplier),功能與 orElseThrow 類似,如果值存在則返回,否則拋出 exceptionSupplier 返回的異常,定義如下所示。
[Java] 純文本查看 複製代碼
1
2
3
4
5
6
7
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
 
具體操作如下所示。
[Java] 純文本查看 複製代碼
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
public class Test {
public static void main(String[] args) {
Optional optional = Optional.ofNullable(1);
try {
System.out.println(optional.orElseThrow(()->{throw new IllegalStateException();}));
} catch (Throwable throwable) {
throwable.printStackTrace();
}
optional = Optional.ofNullable(null);
try {
System.out.println(optional.orElseThrow(()->{throw new IllegalStateException();}));
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}


 
上面詳細介紹了 Optional 類的各種方法,接下來我們結合實際案例,來看看實際開發中使用 Optional 的優勢。
實際案例
我們來設置一個客戶訂單查詢場景。
1、定義 3 個類 Consumer、Order、Product,其中 Consumer 包含 Order,Order 包含 Product,具體代碼如下所示。
[Java] 純文本查看 複製代碼
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Consumer {
private Order order;
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
}
public class Order {
private Product product;
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
}
public class Product {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
 
2、分別創建 Consumer、Order、Product 對象,並完成級聯,然後定義返回 Consumer 中 ProductName 的方法,傳統的開發方式需要對涉及到的對象都進行非空驗證,如下所示。
[Java] 純文本查看 複製代碼
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Test {
public static void main(String[] args) {
Product product = new Product();
product.setName("電腦");
Order order = new Order();
order.setProduct(product);
Consumer consumer = new Consumer();
consumer.setOrder(order);
System.out.println(getProductName(consumer));
}
public static String getProductName(Consumer consumer){
if(consumer != null){
Order order = consumer.getOrder();
if(order != null){
Product product = order.getProduct();
if(product != null){
return product.getName();
}
}
}
return null;
}
}
 
3、使用 Optional 類對上述代碼進行修改,可以將代碼變得更加簡潔、優雅,不需要一層一層的寫非空判斷,可直接完成鏈式調用,如下所示。
[Java] 純文本查看 複製代碼
1
2
3
4
5
6
7
public static String getProductName(Consumer consumer){
return Optional.ofNullable(consumer)
.map(consumer1 -> consumer1.getOrder())
.map(order -> order.getProduct())
.map(product -> product.getName())
.orElse(null);
}
發佈了648 篇原創文章 · 獲贊 9 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章