摘要:于是,在引入了,用來代表一種可能有可能沒有的數據,可以用來緩解空指針異常的問題。
一位智者說過,沒有處理過空指針異常就不算一個真正的 Java 程序員。這當然是開玩笑,但是空指針異常確實是很多程序出錯的源頭。
于是,在 Java 8 引入了 java.util.Optional,Optional 用來代表一種 可能有可能沒有 的數據,可以用來緩解空指針異常的問題。
簡單地說,Optional 用來避免這種代碼:
String version = "UNKNOWN"; if(computer != null){ Soundcard soundcard = computer.getSoundcard(); if(soundcard != null){ USB usb = soundcard.getUSB(); if(usb != null){ version = usb.getVersion(); } } }
如果用 Optional 表示呢?大概是這樣:
String version = computer.flatMap(Computer::getSoundcard) .flatMap(Soundcard::getUSB) .map(USB::getVersion) .orElse("UNKNOWN");
實際上,Optional 即函數式編程中的 Maybe。
以下示例在 OptionalTest.java 中。
創建創建 Optional 有三種方式,分別是 empty、 of 和 ofNullable。
emptyempty 用來創建一個空的 Optional
@Test public void create_optional_with_empty() { Optionalofempty = Optional.empty(); assertFalse(empty.isPresent()); }
of 用來創建一個非空的 Optional:
@Test public void create_optional_with_of() { Optionaljava = Optional.of("Java"); assertTrue(java.isPresent()); }
但是參數不能為 null,否則會拋空指針異常:
@Test(expected = NullPointerException.class) public void create_optional_with_of_with_null() { Optional.of(null); }ofNullable
ofNullable 用來創建一個可能為空的 Optional:
@Test public void create_optional_with_ofNullable() { Optional檢測值是否存在java = Optional.ofNullable("Java"); assertTrue(java.isPresent()); Optional
可以使用 isPresent 和 isEmpty 判斷 Optional 的值是否為空。
isPresent如果 Optional 中值非 null,則返回 true,否則返回 false。
@Test public void check_optional_with_isPresent() { OptionalisEmptyjava = Optional.ofNullable("java"); Optional
Java 11 開始可以使用 isEmpty。
isEmpty 與 isPresent 相反,如果為 null 返回 true。
@Test public void check_optional_with_isEmpty() { Optional條件動作java = Optional.ofNullable("java"); Optional
關于條件的動作有 ifPresent、orElse、orElseGet、orElseThrow、or、ifPresentOrElse,它們執行與否取決于 Optional 的值是否為 null。
為了避免空指針異常,我們會經常寫下面的代碼:
if (name != null){ System.out.println(name.length); }
Optional 使用一種函數式的方式來替代上面的寫法。
ifPresentifPresent 接受一個 Consumer,在 Optional 值非 null 時調用,并接受 Optional 的值。
@Test public void condition_action_ifPresent() { OptionalorElsejava = Optional.ofNullable("java"); java.ifPresent((value) -> System.out.println("ifPresent accept " + value)); Optional
orElse 在 Optional 值為 null 時觸發,它接受一個參數,作為 Optional 的默認值。
@Test public void condition_action_orElse() { assertTrue(Optional.ofNullable("java").orElse("javascript").equals("java")); assertTrue(Optional.ofNullable(null).orElse("java").equals("java")); }orElseGet
orElseGet 與 orElse 類似,但 orElseGet 接受的是一個 Supplier,Supplier 返回的值作為 Optional 的默認值。
@Test public void condition_action_orElseGet() { assertTrue(Optional.ofNullable("java").orElseGet(() -> "javascript").equals("java")); assertTrue(Optional.ofNullable(null).orElseGet(() -> "java").equals("java")); }orElse 和 orElseGet 的區別
orElse 和 orElseGet 的函數簽名是不一樣的,但如果想使用同樣的函數的返回值來作為 Optional 的默認值,我們很可能會這么干:
public String getDefaultName() { System.out.println("You got a default name"); return "default"; } @Test public void difference_between_orElse_and_orElseGet() { Optionaljava = Optional.of("java"); System.out.println("orElse:"); assertEquals("java", java.orElse(getDefaultName())); System.out.println("orElseGet:"); assertEquals("java", java.orElseGet(this::getDefaultName)); }
若 java 是 null,則 orElse 和 orElseGet 沒有什么不同,getDefaultName 方法都會執行并將返回值作為 Optional 的默認值。
當在上面的例子中,java 非 null,這時 orElse 的 getDefaultName 還是會執行,但 orElseGet 不會。輸出:
orElse: You got a default name orElseGet:
當 getDefaultName 中有副作用或耗時操作時需要注意。
orElseThroworElseThrow 與 orElse 一樣也在當 Optional 值為 null 時觸發,但與之不同的是會拋出指定的異常:
@Test(expected = IllegalArgumentException.class) public void condition_action_orElseThrow() { Optional.ofNullable(null).orElseThrow(IllegalArgumentException::new); }or
or 是 Java 9 中新增方法。與 orElseGet 很相似,or 也接受一個 Supplier,但 or 返回的是一個新的 Optional。
@Test public void condition_or_optional() { OptionalifPresentOrElsejava = Optional.of("java") .or(() -> Optional.of("javascript")); Optional
ifPresentOrElse 是 Java 9 中新增的方法。ifPresent 就如同命令式編程中的 if-else,它接受兩個參數,第一個為 Consumer,在 Optional 有值時調用,第二個為 Runnable,在無值時調用:
@Test public void condition_ifPresentOrElse() { // value is java Optional.of("java") .ifPresentOrElse(value -> System.out.println("value is " + value), () -> System.out.println("ooops")); // ooops Optional.empty() .ifPresentOrElse(value -> System.out.println("value is " + value), () -> System.out.println("ooops")); }獲取值
Optional 提供了一個 get 方法獲取值,但 get 方法只能在 Optional 有值時使用,否則會拋出 NoSuchElementException 異常:
@Test public void get_optional_with_of() { Optional驗證值java = Optional.of("Java"); assertEquals("java", java.get()); } @Test(expected = NoSuchElementException.class) public void get_optional_with_of_with_null() { Optional.empty().get(); }
filter 方法用來驗證 Optional 的值是否符合條件,它接受一個 Predicate 作為參數。如果 Optional 的值為 null 或 Predicate 判斷不通過,則返回 empty;否則返回該 Optional。
@Test public void test_optional_by_filter() { Integer nullYear = null; Optionalinteger = Optional.ofNullable(nullYear) .filter(value -> value == 2018); assertEquals(Optional.empty(), integer); Integer year = 2019; Optional integer1 = Optional.ofNullable(year) .filter(value -> value == 2018); assertEquals(Optional.empty(), integer1); Optional integer2 = Optional.ofNullable(year) .filter(value -> value == 2019); assertEquals("Optional[2019]", integer2.toString()); }
filter 相對傳統 if 而言省去了很多樣板代碼,如:
public boolean priceIsInRange1(Modem modem) { boolean isInRange = false; if (modem != null && modem.getPrice() != null && (modem.getPrice() >= 10 && modem.getPrice() <= 15)) { isInRange = true; } return isInRange; }
使用 Optional 實現同樣的方法:
public boolean priceIsInRange2(Modem modem2) { return Optional.ofNullable(modem2) .map(Modem::getPrice) .filter(p -> p >= 10) .filter(p -> p <= 15) .isPresent(); }處理值
處理值的方式有 map 和 flatMap。
map使用 map 可以對 Optional 中的值進行處理并返回。
@Test public void map_optional() { OptionalflatMapjava = Optional.of("java"); String result = java.map(String::toUpperCase).orElse(""); assertEquals("JAVA", result); }
flatMap 與 map 的區別在于 map 處理值后會包裝返回值,而 flatMap 不包裝。
public class Person { private String name; public Person(String name) { this.name = name; } public Optional流操作getName() { return Optional.ofNullable(name); } } @Test public void flatMap_optional() { Person person = new Person("john"); Optional personOptional = Optional.of(person); String byMap = personOptional.map(Person::getName) // 需要手動打開包裝 .orElse(Optional.empty()) .orElse(""); String byFlatMap = personOptional.flatMap(Person::getName) .orElse(""); assertEquals("john", byMap); assertEquals("john", byFlatMap); }
在 Java 9 中,新增了 stream 方法,可以對 Optional 創建 stream,然后可以使用 stream 上的所有方法。
如果 Optional 為 empty,則創建一個 empty 的 stream。
@Test public void treat_optional_as_stream() { Listcollect = Optional.of("java") .stream() .map(value -> value.concat("script")) .collect(Collectors.toList()); assertArrayEquals(new String[]{"javascript"}, collect.toArray()); // empty optional Optional value = Optional.empty(); List emptyStream = value.stream() .map(String::toUpperCase) .collect(Collectors.toList()); assertEquals(0, emptyStream.size()); }
所以使用 stram 也可以篩出非 null 的 Optional 的值:
@Test public void filter_empty_by_stream() { List參考> languages = List.of(Optional.of("java"), Optional.empty(), Optional.empty(), Optional.of("javascript")); List collect = languages.stream() .flatMap(Optional::stream) .collect(Collectors.toList()); assertArrayEquals(new String[]{"java", "javascript"}, collect.toArray()); }
Tired of Null Pointer Exceptions? Consider Using Java SE 8"s Optional!
Guide To Java 8 Optional
Java 9 Optional API Additions
Filtering a Stream of Optionals in Java
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/73861.html
摘要:新特性總覽標簽本文主要介紹的新特性,包括表達式方法引用流默認方法組合式異步編程新的時間,等等各個方面。還有對應的和類型的函數連接字符串廣義的歸約匯總起始值,映射方法,二元結合二元結合。使用并行流時要注意避免共享可變狀態。 Java8新特性總覽 標簽: java [TOC] 本文主要介紹 Java 8 的新特性,包括 Lambda 表達式、方法引用、流(Stream API)、默認方...
摘要:在目前的工作中,我對中的和表達式都使用得很多,之前也寫了兩篇文章來總結對應的知識。為了避免,他們會加很多判斷語句,使得代碼的可讀性變得很差。在后面的開發中,可以使用設計,這樣可以設計出更安全的接口和方法。 showImg(https://upload-images.jianshu.io/upload_images/44770-dfeb2d48d3779723.jpg?imageMogr...
摘要:用于對流進行排序。三最終操作用于迭代流中的每個元素,并執行相應的操作。類類也是的新特性,用于有效解決問題。與的功能一樣,不過接受一個函數式接口來生成對象為空則拋出異常與使用類似與使用類似這是一種級聯的方式,能夠解決方式的嵌套問題。 Stream API Stream API是Java8的新特性,通常用于對集合進行一些操作,可以幫助開發者寫出更高效、整潔的代碼。 一、Stream流的創建...
摘要:于是抽時間看了看以后各個版本的特性,做了一個總結。年和公開版本發布,取名為。此后對應版本就是,。發布,是一個重大版本更新。在此之后,就是每六個月發布一次新版本。以上和參考資料聊了一些關于的歷史,下面我們看看各個版本有那些新特性。 【這是 ZY 第 11 篇原創技術文章】 某天在網上閑逛,突然看到有篇介紹 Java 11 新特性的文章,頓時心里一驚,畢竟我對于 Java 的版本認識...
閱讀 2879·2021-11-24 09:39
閱讀 3130·2021-11-19 10:00
閱讀 1535·2021-10-27 14:17
閱讀 1811·2021-10-14 09:43
閱讀 961·2021-09-03 10:30
閱讀 3413·2019-08-30 15:54
閱讀 2728·2019-08-30 13:05
閱讀 2006·2019-08-30 11:02