原文地址:https://www.baeldung.com/java-optional 只是將其翻譯一遍,加上些本身的理解,順便總結一下java
一 概覽web
Optional是java.util包中的一部分,所以爲了使用Optional,須要:oracle
import java.util.Optional;
二 建立app
2.1 調用empty API, 建立一個空的Optional對象:函數
@Test public void whenCreatesEmptyOptional_thenCorrect() { Optional<String> empty = Optional.empty(); assertFalse(empty.isPresent()); }
Ps: isPresent API 是用來檢查Optional對象中是否有值。只有當咱們建立了一個含有非空值的Optional時才返回true。在下一部分咱們將介紹這個API。測試
2.2 使用staticAPI建立this
@Test public void givenNonNull_whenCreatesOptional_thenCorrect() { String name = "baeldung"; Optional<String> opt = Optional.of(name); assertEquals("Optional[baeldung]", opt.toString()); }
然而,傳遞給of()的值不能夠爲空,不然會拋出空指針異常,以下:spa
@Test(expected = NullPointerException.class) public void givenNull_whenThrowsErrorOnCreate_thenCorrect() { String name = null; Optional<String> opt = Optional.of(name); }
有時咱們須要傳遞一些空值,那咱們可使用下面這個API:翻譯
@Test public void givenNonNull_whenCreatesNullable_thenCorrect() { String name = "baeldung"; Optional<String> opt = Optional.ofNullable(name); assertEquals("Optional[baeldung]", opt.toString()); }
使用ofNullable API,則當傳遞進去一個空值時,不會拋出異常,而只是返回一個空的Optional對象,如同咱們用Optional.empty API:指針
@Test public void givenNull_whenCreatesNullable_thenCorrect() { String name = null; Optional<String> opt = Optional.ofNullable(name); assertEquals("Optional.empty", opt.toString()); }
三 使用isPresent API 檢查值
咱們可使用這個API檢查一個Optional對象中是否有值,只有值非空才返回true。
@Test public void givenOptional_whenIsPresentWorks_thenCorrect() { Optional<String> opt = Optional.of("Baeldung"); assertTrue(opt.isPresent()); opt = Optional.ofNullable(null); assertFalse(opt.isPresent()); }
四 適當狀況下使用isPresent API
傳統上,咱們通常這樣寫來檢查空值:
if(name != null){ System.out.println(name.length); }
問題在於,有時候咱們會忘記了對空值進行檢查,這時就可使用這個API:
@Test public void givenOptional_whenIfPresentWorks_thenCorrect() { Optional<String> opt = Optional.of("baeldung"); opt.ifPresent(name -> System.out.println(name.length())); }
五 orEse && orElseGet
5.1 orElse
這個API被用來檢索Optional對象中的值,它被傳入一個「默認參數‘。若是對象中存在一個值,則返回它,不然返回傳入的「默認參數」,以下所示:
@Test public void whenOrElseWorks_thenCorrect() { String nullName = null; String name = Optional.ofNullable(nullName).orElse("john"); assertEquals("john", name); }
5.2 orElseGet
與orElsel相似,可是這個函數不接收一個「默認參數」,而是一個函數接口,以下例所示:
@Test public void whenOrElseGetWorks_thenCorrect() { String nullName = null; String name = Optional.ofNullable(nullName).orElseGet(() -> "john"); assertEquals("john", name); }
5.3 二者區別
要想理解這兩者,首先讓咱們建立一個無參且返回定值的方法:
public String getMyDefault() { System.out.println("Getting Default Value"); return "Default Value"; }
接下來,進行兩個測試看看它們有什麼區別:
@Test public void whenOrElseGetAndOrElseOverlap_thenCorrect() { String text; System.out.println("Using orElseGet:"); String defaultText = Optional.ofNullable(text).orElseGet(this::getMyDefault); assertEquals("Default Value", defaultText); System.out.println("Using orElse:"); defaultText = Optional.ofNullable(text).orElse(getMyDefault()); assertEquals("Default Value", defaultText); }
在這裏示例中,咱們的Optional對象中包含的都是一個空值,讓咱們看看程序執行結果:
Using orElseGet: Getting default value... Using orElse: Getting default value...
兩個Optional對象中都不存在value,所以執行結果相同。
那麼當Optional對象中值存在時又是怎樣呢?
@Test public void whenOrElseGetAndOrElseDiffer_thenCorrect() { String text = "Text present"; System.out.println("Using orElseGet:"); String defaultText = Optional.ofNullable(text).orElseGet(this::getMyDefault); assertEquals("Text present", defaultText); System.out.println("Using orElse:"); defaultText = Optional.ofNullable(text).orElse(getMyDefault()); assertEquals("Text present", defaultText); }
讓咱們看看執行結果:
Using orElseGet: Using orElse: Getting default value...
能夠看到,當使用orElseGet去檢索值時,getMyDefault並不執行,由於Optional中含有值,而使用orElse時則照常執行。因此能夠看到,當值存在時,orElse相比於orElseGet,多建立了一個對象,可能從這個實例中你感覺不到影響有多大,但考慮當getDefalut不只僅是個簡單函數,而是一個web service之類的,則多建立一個代價是比較大的。
六 orElseThrow
orElseThrow當遇到一個不存在的值的時候,並不返回一個默認值,而是拋出異常,以下所示:
@Test(expected = IllegalArgumentException.class) public void whenOrElseThrowWorks_thenCorrect() { String nullName = null; String name = Optional.ofNullable(nullName).orElseThrow( IllegalArgumentException::new); }
七 使用get()
@Test public void givenOptional_whenGetsValue_thenCorrect() { Optional<String> opt = Optional.of("baeldung"); String name = opt.get(); assertEquals("baeldung", name); }
使用get() API 也能夠返回被包裹着的值。可是必須是值存在時,當值不存在時,它會拋出一個NoSuchElementException異常,以下所示:
@Test(expected = NoSuchElementException.class) public void givenOptionalWithNull_whenGetThrowsException_thenCorrect() { Optional<String> opt = Optional.ofNullable(null); String name = opt.get(); }
由於這個方法與咱們使用Optional的目的相違背,因此能夠預見在不久未來它或許會被拋棄,建議仍是使用其餘的方法。
八 filter()
接收一個函數式接口,當符合接口時,則返回一個Optional對象,不然返回一個空的Optional對象。
@Test public void whenOptionalFilterWorks_thenCorrect() { Integer year = 2016; Optional<Integer> yearOptional = Optional.of(year); boolean is2016 = yearOptional.filter(y -> y == 2016).isPresent(); assertTrue(is2016); boolean is2017 = yearOptional.filter(y -> y == 2017).isPresent(); assertFalse(is2017); }
這個API做用通常就是拒絕掉不符合條件的,好比拒絕掉錯誤的電子郵箱。
讓咱們看下一個更有意義的例子,假如咱們想買一個調制解調器,而且只關心它的價格:
public class Modem { private Double price; public Modem(Double price) { this.price = price; } //standard getters and setters }
接下來,咱們想要檢查每一類調制解調器是否在咱們能夠承受的價格範圍內,那咱們在不使用Optional時該如何作呢?
public boolean priceIsInRange1(Modem modem) { boolean isInRange = false; if (modem != null && modem.getPrice() != null && (modem.getPrice() >= 10 && modem.getPrice() <= 15)) { isInRange = true; } return isInRange; }
咱們居然要寫這麼多code,尤爲是if條件語句,然而對於這部分code最關鍵的實際上是對價格範圍的判斷。
這是一個Test例子:
@Test public void whenFiltersWithoutOptional_thenCorrect() { assertTrue(priceIsInRange1(new Modem(10.0))); assertFalse(priceIsInRange1(new Modem(9.9))); assertFalse(priceIsInRange1(new Modem(null))); assertFalse(priceIsInRange1(new Modem(15.5))); assertFalse(priceIsInRange1(null)); }
若是長時間不用,那麼有可能會忘記對null進行檢查,那麼若是使用Optional,會怎麼樣呢?
public boolean priceIsInRange2(Modem modem2) { return Optional.ofNullable(modem2) .map(Modem::getPrice) .filter(p -> p >= 10) .filter(p -> p <= 15) .isPresent(); }
map()僅僅是將一個值轉換爲另外一個值,請謹記在心,這個操做並不會改變原來的值。
讓咱們仔細看看這段代碼,首先,當咱們傳入一個null時,不會發生任何問題。其次,咱們在這段code所寫的惟一邏輯就如同此方法名所描述。
以前的那段code爲了其固有的脆弱性,必須作更多,而如今不用了。
九 map()
在以前的例子中,咱們使用filter()來接受/拒絕一個一個值,而使用map()咱們能夠將一個值轉換爲另外一個值。
@Test public void givenOptional_whenMapWorks_thenCorrect() { List<String> companyNames = Arrays.asList( "paypal", "oracle", "", "microsoft", "", "apple"); Optional<List<String>> listOptional = Optional.of(companyNames); int size = listOptional .map(List::size) .orElse(0); assertEquals(6, size); }
在這個例子中,咱們使用一個List包含了一些字符串,而後再把這個List包裹起來,對其map(),咱們這裏是對這個List求它的size。
map()返回的結果也被包裹在一個Optional對象中,咱們必須調用合適的方法來查看其中的值。
注意到filter()僅僅是對值進行一個檢查並返回一個boolean(很奇怪,照前面所述不該返回一個Optional對象嗎?),而map()是使用現有的值進行計算,而且返回一個包裹着計算結果(映射結果)的Optional對象。
@Test public void givenOptional_whenMapWorks_thenCorrect2() { String name = "baeldung"; Optional<String> nameOptional = Optional.of(name); int len = nameOptional .map(String::length()) .orElse(0); assertEquals(8, len); }
將filter()與map()一塊兒使用能夠作一些很強力的事情。
假設咱們如今要檢查一個用戶的密碼,那麼咱們能夠這樣作:
@Test public void givenOptional_whenMapWorksWithFilter_thenCorrect() { String password = " password "; Optional<String> passOpt = Optional.of(password); boolean correctPassword = passOpt.filter( pass -> pass.equals("password")).isPresent(); assertFalse(correctPassword); correctPassword = passOpt .map(String::trim) .filter(pass -> pass.equals("password")) .isPresent(); assertTrue(correctPassword); }
注意到,若是不進行trim,則會返回false,這裏咱們可使用map()進行trim。
十 flatmap()
有時咱們可使用flatmap()替換map(),兩者不一樣之處在於,map()只有當值不被包裹時才進行轉換,而flatmap()接受一個被包裹着的值而且在轉換以前對其解包。
咱們如今有一個Person類:
public class Person { private String name; private int age; private String password; public Optional<String> getName() { return Optional.ofNullable(name); } public Optional<Integer> getAge() { return Optional.ofNullable(age); } public Optional<String> getPassword() { return Optional.ofNullable(password); } // normal constructors and setters }
咱們能夠像對待String同樣將其包裹起來:
Person person = new Person("john", 26); Optional<Person> personOptional = Optional.of(person);
注意當咱們包裹一個Person對象時,它將包含一個嵌套的Optional例子:
@Test public void givenOptional_whenFlatMapWorks_thenCorrect2() { Person person = new Person("john", 26); Optional<Person> personOptional = Optional.of(person); Optional<Optional<String>> nameOptionalWrapper = personOptional.map(Person::getName); Optional<String> nameOptional = nameOptionalWrapper.orElseThrow(IllegalArgumentException::new); String name1 = nameOptional.orElse(""); assertEquals("john", name1); String name = personOptional .flatMap(Person::getName) .orElse(""); assertEquals("john", name); }
須要注意,方法getName返回的是一個Optional對象,而不是像trim那樣。這樣就生成了一個嵌套的Optional對象。
所以使用map,咱們還須要再解包一次,而使用flatMap()就不須要了。
錯誤不當之處請指出,謝謝。