本帖最後由 小刀葛小倫 於 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 );
}
|
|