Optional 類是一個能夠爲null的容器對象。若是值存在則isPresent()方法會返回true,調用get()方法會返回該對象。
Optional 是個容器:它能夠保存類型T的值,或者僅僅保存null。Optional提供不少有用的方法,這樣咱們就不用顯式進行空值檢測。
Optional 類的引入很好的解決空指針異常。html
package com.demo.optional; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.Optional; @Slf4j @RunWith(SpringRunner.class) @SpringBootTest public class TestOptional { @Test public void testOptional() { TestOptional test = new TestOptional(); Integer value1 = null; Integer value2 = new Integer(10); // Optional.ofNullable - 容許傳遞爲 null 參數 Optional<Integer> a = Optional.ofNullable(value1); // Optional.of - 若是傳遞的參數是 null,拋出異常 NullPointerException Optional<Integer> b = Optional.of(value2); log.info("{}", test.sum(a, b)); } public Integer sum(Optional<Integer> a, Optional<Integer> b) { // Optional.isPresent - 判斷值是否存在 log.info("第一個參數值存在: {}", a.isPresent()); log.info("第二個參數值存在: {}", b.isPresent()); // Optional.orElse - 若是值存在返回它,不然返回默認值 Integer value1 = a.orElse(new Integer(0)); //Optional.get - 獲取值,值須要存在 Integer value2 = b.get(); return value1 + value2; } }
在Java8以前,任何訪問對象方法或屬性的調用均可能致使 NullPointerException。若是須要確保不觸發異常,就得在訪問每個值以前對其進行明確地檢查。java
一、明確對象不爲null的時候使用of()。若是對象便可能是null也多是非null,你就應該使用ofNullable()方法。spring
package com.demo.optional; import com.demo.optional.domain.User; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.NoSuchElementException; import java.util.Optional; import static junit.framework.TestCase.assertTrue; import static org.junit.Assert.assertEquals; @Slf4j @RunWith(SpringRunner.class) @SpringBootTest public class OptionalTest { @Test(expected = NoSuchElementException.class) public void whenCreateEmptyOptionalThenNull() { Optional<User> emptyOpt = Optional.empty(); emptyOpt.get(); } //明確對象不爲null的時候使用of() @Test(expected = NullPointerException.class) public void whenCreateOfEmptyOptionalThenNullPointerException() { User user = null; Optional<User> opt = Optional.of(user); } //若是對象便可能是null也多是非null,你就應該使用ofNullable()方法 @Test public void whenCreateOfEmptyOptionalThenNoNullPointerException() { User user = null; Optional<User> opt = Optional.ofNullable(user); } }
二、訪問Optional對象的值dom
package com.demo.optional; import com.demo.optional.domain.User; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.NoSuchElementException; import java.util.Optional; import static junit.framework.TestCase.assertTrue; import static org.junit.Assert.assertEquals; @Slf4j @RunWith(SpringRunner.class) @SpringBootTest public class OptionalTest { //從Optional實例中取回實際值對象的方法之一是使用get()方法 @Test public void whenCreateOfNullableOptionalThenOk() { String name = "John"; Optional<String> opt = Optional.ofNullable(name); assertEquals("John", opt.get()); } //get()方法會在值爲null的時候拋出異常。要避免異常,你能夠選擇首先驗證是否有值 @Test public void whenCheckIsPresentThenOk() { User user = new User("john@gmail.com", "1234"); Optional<User> opt = Optional.ofNullable(user); assertTrue(opt.isPresent()); assertEquals(user.getEmail(), opt.get().getEmail()); } //檢查是否有值的另外一個選擇是ifPresent()方法。該方法除了執行檢查,還接受一個Consumer(消費者)參數,若是對象不是空的,就對執行傳入的Lambda表達式 @Test public void whenCheckIfPresentThenOk() { User user = new User("john@gmail.com", "1234"); Optional<User> opt = Optional.ofNullable(user); opt.ifPresent(u -> assertEquals(user.getEmail(), u.getEmail())); } }
三、返回默認值:Optional類提供了API用以返回對象值,或者在對象爲空的時候返回默認值。函數
package com.demo.optional; import com.demo.optional.domain.User; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.Optional; import static org.junit.Assert.assertEquals; @Slf4j @RunWith(SpringRunner.class) @SpringBootTest public class OptionalTest { //orElse()若是有值則返回該值,不然返回傳遞給它的參數值 @Test public void whenEmptyValueThenReturnDefault() { User user = null; User user2 = new User("anna@gmail.com", "1234"); User result = Optional.ofNullable(user).orElse(user2); assertEquals(user2.getEmail(), result.getEmail()); } //若是對象的初始值不是 null,那麼默認值會被忽略 @Test public void whenValueNotNullThenIgnoreDefault() { User user = new User("john@gmail.com", "1234"); User user2 = new User("anna@gmail.com", "1234"); User result = Optional.ofNullable(user).orElse(user2); assertEquals("john@gmail.com", result.getEmail()); } //orElseGet()方法會在有值的時候返回值,若是沒有值,它會執行做爲參數傳入的Supplier(供應者)函數式接口,並將返回其執行結果 @Test public void testOrElseGet() { User user = new User("john@gmail.com", "1234"); User user2 = new User("anna@gmail.com", "1234"); User result = Optional.ofNullable(user).orElseGet(() -> user2); assertEquals("john@gmail.com", result.getEmail()); } }
四、orElse()和orElseGet()的不一樣之處性能
package com.demo.optional; import com.demo.optional.domain.User; import jdk.nashorn.internal.runtime.regexp.joni.constants.OPCode; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.NoSuchElementException; import java.util.Optional; import static junit.framework.TestCase.assertTrue; import static org.junit.Assert.assertEquals; @Slf4j @RunWith(SpringRunner.class) @SpringBootTest public class OptionalTest { /* * 對象爲空時,兩種方法都調用了createNewUser()方法,這個方法會記錄一個消息並返回User對象。 * 當對象爲空而返回默認對象時,行爲並沒有差別。 */ @Test public void givenEmptyValueWhenCompareThenOk() { User user = null; log.info("Using orElse"); User result = Optional.ofNullable(user).orElse(createNewUser()); log.info("User orElseGet"); User result2 = Optional.ofNullable(user).orElseGet(() -> createNewUser()); } /* * 兩個Optional對象都包含非空值,兩個方法都會返回對應的非空值。orElse()方法仍然建立了User對象。與之相反,orElseGet()方法不建立 User 對象。 * 在執行較密集的調用時,好比調用Web服務或數據查詢,這個差別會對性能產生重大影響。 */ @Test public void givenPresentValueWhenCompareThenOk() { User user = new User("john@gmail.com", "1234"); log.info("Using orElse"); User result = Optional.ofNullable(user).orElse(createNewUser()); log.info("Using orElseGet"); User result2 = Optional.ofNullable(user).orElseGet(() -> createNewUser()); } private User createNewUser() { log.info("Creating New User"); return new User("extra@gmail.com", "1234"); } }
五、返回異常測試
除了orElse()和orElseGet()方法,Optional還定義了orElseThrow(),它會在對象爲空的時候拋出異常,而不是返回備選的值。this
package com.demo.optional; import com.demo.optional.domain.User; import jdk.nashorn.internal.runtime.regexp.joni.constants.OPCode; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.NoSuchElementException; import java.util.Optional; import static junit.framework.TestCase.assertTrue; import static org.junit.Assert.assertEquals; @Slf4j @RunWith(SpringRunner.class) @SpringBootTest public class OptionalTest { /* * orElseThrow()方法有更豐富的語義,能夠決定拋出什麼樣的異常,而不老是拋出 NullPointerException。 * 若是 user 值爲 null,會拋出 IllegalArgumentException。 */ @Test(expected = IllegalArgumentException.class) public void whenThrowExceptionThenOk() { User user = null; User result = Optional.ofNullable(user) .orElseThrow(() -> new IllegalArgumentException()); } }
六、轉換值.net
package com.demo.optional; import com.demo.optional.domain.User; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.Optional; import static org.junit.Assert.assertEquals; @Slf4j @RunWith(SpringRunner.class) @SpringBootTest public class OptionalTest { //若是建立的Optional中的值存在,對該值執行提供的Function函數調用 @Test public void whenMapThenOk() { User user = new User("anna@gmail.com", "1234"); String email = Optional.ofNullable(user) .map(u -> u.getEmail()).orElse("default@gmail.com"); assertEquals(email, user.getEmail()); } //若是建立的Optional中的值存在,就對該值執行提供的Function函數調用,返回一個Optional類型的值,不然就返回一個空的Optional對象 @Test public void whenFlatMapThenOk() { User user = new User("anna@gmail.com", "1234"); user.setPosition("Developer"); String position = Optional.ofNullable(user) .flatMap(u -> u.getPosition()).orElse("default"); assertEquals(position, user.getPosition().get()); } }
七、過濾值指針
filter()接受一個Predicate參數,返回測試結果爲true的值。若是測試結果爲false,會返回一個空的Optional。
package com.demo.optional; import com.demo.optional.domain.User; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.Optional; import static junit.framework.TestCase.assertTrue; @Slf4j @RunWith(SpringRunner.class) @SpringBootTest public class OptionalTest { @Test public void whenFilterThenOk() { User user = new User("anna@gmail.com", "1234"); Optional<User> result = Optional.ofNullable(user) .filter(u -> u.getEmail() != null && u.getEmail().contains("@")); assertTrue(result.isPresent()); } }
八、Optional類的鏈式方法
爲了更充分的使用Optional,你能夠連接組合其大部分方法,由於它們都返回相同相似的對象。
package com.demo.optional; import com.demo.optional.domain.Address; import com.demo.optional.domain.Country; import com.demo.optional.domain.User; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.Optional; import static org.junit.Assert.assertEquals; @Slf4j @RunWith(SpringRunner.class) @SpringBootTest public class OptionalTest { @Test public void whenChainingThenOk() { User user = new User("anna@gmail.com", "1234"); String result = Optional.ofNullable(user) .flatMap(u -> u.getAddress()) .flatMap(a -> a.getCountry()) .map(c -> c.getIsocode()) .orElse("default"); String res = Optional.ofNullable(user) .flatMap(User::getAddress) .flatMap(Address::getCountry) .map(Country::getIsocode) .orElse("default"); assertEquals(result, "default"); assertEquals(res, "default"); } }
實體類User:
package com.demo.optional.domain; import lombok.Data; import java.util.Optional; @Data public class User { private Integer id; private String email; private String number; private String position; private Address address; public Optional<Address> getAddress() { return Optional.ofNullable(address); } public Optional<String> getPosition() { return Optional.ofNullable(position); } public User(String email, String number) { this.email = email; this.number = number; } }
實體類Address:
package com.demo.optional.domain; import lombok.Data; import java.util.Optional; @Data public class Address { private Country country; public Optional<Country> getCountry() { return Optional.ofNullable(country); } }
實體類Country:
package com.demo.optional.domain; import lombok.Data; @Data public class Country { private Integer id; private String isocode; }
九、Java9加強
Java9爲Optional類添加了三個方法:or()、ifPresentOrElse() 和 stream()。
or()方法與orElse()和orElseGet()相似,它們都在對象爲空的時候提供了替代狀況。
or()的返回值是由Supplier參數產生的另外一個Optional對象。若是對象包含值,則Lambda表達式不會執行。
package com.demo.optional; import com.demo.optional.domain.User; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.Optional; import static org.junit.Assert.assertEquals; @Slf4j @RunWith(SpringRunner.class) @SpringBootTest public class OptionalTest { @Test public void whenEmptyOptionalThenGetValueFromOr() { User user=null; User result = Optional.ofNullable(user) .or( () -> Optional.of(new User("default","1234"))).get(); assertEquals(result.getEmail(), "default"); } }
ifPresentOrElse()方法須要兩個參數:一個Consumer和一個Runnable。若是對象包含值,會執行Consumer的動做,不然運行Runnable。
若是想在有值的時候執行某個動做,或者只是跟蹤是否認義了某個值,那麼這個方法很是有用。
package com.demo.optional; import com.demo.optional.domain.User; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.Optional; @Slf4j @RunWith(SpringRunner.class) @SpringBootTest public class OptionalTest { @Test public void testIfPresentOrElse() { User user = new User("anna@gmail.com", "1234"); Optional.ofNullable(user).ifPresentOrElse(u -> log.info("User is:" + u.getEmail()), () -> log.info("User not found")); } }
stream()方法把實例轉換爲Stream對象。Stream的使用帶來了其filter()、map()和collect()接口,以獲取List。
package com.demo.optional; import com.demo.optional.domain.Address; import com.demo.optional.domain.Country; import com.demo.optional.domain.User; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.List; import java.util.NoSuchElementException; import java.util.Optional; import java.util.stream.Collectors; import static junit.framework.TestCase.assertTrue; import static org.junit.Assert.assertEquals; @Slf4j @RunWith(SpringRunner.class) @SpringBootTest public class OptionalTest { @Test public void testStream() { User user = new User("john@gmail.com", "1234"); List<String> emails = Optional.ofNullable(user) .stream() .filter(u -> u.getEmail() != null && u.getEmail().contains("@")) .map(u -> u.getEmail()) .collect(Collectors.toList()); assertTrue(emails.size() == 1); assertEquals(emails.get(0), user.getEmail()); } }
Optional不是Serializable。所以,它不該該用做類的字段。
若是須要序列化的對象包含Optional值,Jackson庫支持把Optional看成普通對象。也就是說,Jackson會把空對象看做null,而有值的對象則把其值看做對應域的值。這個功能在jackson-modules-java8項目中。
參考:
https://www.cnblogs.com/zhangboyu/p/7580262.html
https://blog.csdn.net/zknxx/article/details/78586799