【Java 8系列】Java开发者的判空利器 -- Optional
熱門系列:
-
【Java 8系列】收集器Collector與工具類Collectors
-
【Java 8系列】Stream詳解,看這一篇就夠啦
-
【Java 8系列】Lambda 表達式,一看就廢
-
【Java 8系列】Java日期時間的新主宰者:LocalDate、LocalTime、LocalDateTime、ZonedDateTime
-
??程序人生,精彩搶先看
目錄
1.前序
2.正文
2.1?Optional的起源
2.2?Optional使用場景
2.2.1 為什么要用?
2.2.2 什么場景用?
2.3 常用方法使用
2.3.1 empty()
2.3.2?of()
2.3.3?ofNullable()
2.3.4?isPresent()
2.3.5?get()
2.3.6?ifPresent()
2.3.7?filter()
2.3.8?map()
2.3.9?flatMap()
2.3.10?orElse()
2.3.11?orElseGet()
2.3.12?orElseThrow()
3.總結
1.前序
JDK8是Oracle在2014年3月19日發布正式版的,和JDK7(2011年7月發布)相隔了近3年(拖的時間堪比JDK7和JDK6之間的時間,與歷史版本發布間隔相比排在第二位,JDK6發布是2006,JDK7與之相比之間差了5年,這兩個版本發布時間間隔最長,中間發生了Oracle收購SUN的大事件,JDK6因此曾成為使用率最高的JDK,),中間因意見不統一多次延遲。
距離現在已經近6年了。但是,目前仍然有很多公司在使用此版本作為開發版本!(至少我目前所呆的公司仍在使用,各位目前常用的是哪個版本呢?歡迎留言。。。)
但是為什么現在才拿出來記錄呢。。。。當然是因為,我樂意啊,,哈哈。。(其實是最近有時間了,想要做一個Java 8系列的博文集啦)
最近,我會不定期,持續輸出Java 8新特性且常用的一些技術點,例如Optional、Lambda表達式和函數式接口、Stream、日期時間API、重復注釋、類型推斷、?注解的擴展等等,與大家一起分享并記錄!感興趣,可以插個眼~~~!好了,今天先和大家一起扒一扒如題的判空類:Optional
2.正文
2.1?Optional的起源
著名的 NullPointerException?是引起系統失敗最常見的原因。很久以前Google Guava項目引入了Optional作為解決空指針異常的一種方式,不贊成代碼被null檢查的代碼污染,期望程序員寫整潔的代碼。受Google Guava的鼓勵,Optional?現在是Java 8庫的一部分。
Optional?只是一個容器,它可以保存一些類型的值或者null。它提供很多有用的方法,如官方文檔API所示:
| static <T>?Optional<T> | empty() 返回一個空Optional實例。 |
| boolean | equals(Object?obj) 指示其他某個對象是否“等于”此Optional。 |
| Optional<T> | filter(Predicate<? super?T>?predicate) 如果存在一個值,并且該值與給定的謂詞匹配,則返回一個Optional描述值的描述,否則返回一個empty?Optional。 |
| <U>?Optional<U> | flatMap(Function<? super?T,Optional<U>>?mapper) 如果存在值,則將提供的Optional-bearing映射函數應用于該值,返回該結果,否則返回empty?Optional。 |
| T | get() 如果此值存在Optional,則返回該值,否則拋出NoSuchElementException。 |
| int | hashCode() 返回當前值的哈希碼值(如果有);如果沒有值,則返回0(零)。 |
| void | ifPresent(Consumer<? super?T>?consumer) 如果存在值,請使用該值調用指定的使用者,否則不執行任何操作。 |
| boolean | isPresent() true如果存在值,則返回,否則返回false。 |
| <U>?Optional<U> | map(Function<? super?T,? extends U>?mapper) 如果存在值,則將提供的映射函數應用于該值,如果結果為非null,則返回Optional描述結果的描述。 |
| static <T>?Optional<T> | of(T?value) 返回Optional具有指定的當前非空值的。 |
| static <T>?Optional<T> | ofNullable(T?value) 返回Optional描述指定值的描述,如果不為null,則返回null?Optional。 |
| T | orElse(T?other) 返回值(如果存在),否則返回other。 |
| T | orElseGet(Supplier<? extends?T>?other) 返回值(如果存在),否則調用other并返回該調用的結果。 |
| <X extends?Throwable> T | orElseThrow(Supplier<? extends X>?exceptionSupplier) 返回包含的值(如果存在),否則拋出異常,由提供的供應商創建。 |
| String | toString() 返回此Optional的非空字符串表示形式,適用于調試。 |
但實際上,JDK1.8中Optional 總共有17個方法, 除去兩個私有的構造方法, 除去equals, hashCode, toString三個方法外,;只剩下12個 Optional 提供的方法。
?
2.2?Optional使用場景
2.2.1 為什么要用?
在使用一個技術之前,我們必須要做一些思考,這樣是很有必要的!能幫我更好的做到:知其然,知其所以然;(其實說簡單點,就是防止少挖坑,騷年,醒醒吧!!你的bug還沒有修復完~~~)
先說說為什么要用!!試想一下,你的代碼或者你的小伙伴的代碼中,你是否或多或少的見過類似如下的這些代碼:
String isocode = user.getAddress().getCountry().getIsocode().toUpperCase();//將上面的代碼進行判空處理 if (user != null) {Address address = user.getAddress();if (address != null) {Country country = address.getCountry();if (country != null) {String isocode = country.getIsocode();if (isocode != null) {isocode = isocode.toUpperCase();}}} }尤其是,當有很大一段業務邏輯處理的時候,你會發現,代碼不光看起來很臃腫,并且難以閱讀,可讀性很差!那如果我們調整為使用optional來編寫的話,可以轉換成如下寫法:
Optional.ofNullable(user).map(User::getAddress).map(Address::getCountry).map(Country::getIsocode).ifPresent(s-> s.toUpperCase());如此,代碼的可讀性和整潔度,都好了許多!并且使用這種鏈式處理,也能更好的提高開發效率!這也是我們為什么需要用到optional的原因啦!
2.2.2 什么場景用?
我們寫代碼,在保證業務功能的正確和穩定性的基礎之上,才會去考慮代碼的可讀性與簡潔度,而不是一味的追求形式!!否則只會是華而不實,不僅容易出錯,更是本末倒置!
個人總結了幾點如下:
-
當使用值為空的情況,并非源于報錯時產生,可以使用Optional(因為有錯誤的情況,肯定是不正常的,需要處理的)
-
對于一個對象,我們需要做判空、過濾、某些校驗的時候,可以使用Optional
?
2.3 常用方法使用
方法介紹順序,從簡單的開始:
2.3.1 empty()
empty 方法返回一個不包含值的 Optional 實例, 注意不保證返回的 empty 是單例, 不要用 == 比較。
public static<T> Optional<T> empty();?
2.3.2?of()
返回一個 Optional 實例;代表指定的非空值, 如果傳入 null 會立刻拋出空指針異常。
public static <T> Optional<T> of(T value);?
2.3.3?ofNullable()
?
2.3.4?isPresent()
isPresent 用來判斷實例是否包含值, 如果不包含非空值返回 false, 否則返回 true
public boolean isPresent();?
2.3.5?get()
get 方法, 如果實例包含值則返回當前值, 否則拋出 NoSushElementException 異常
public T get();?
2.3.6?ifPresent()
ifPresent 方法作用是當實例包含值時, 來執行傳入的 Consumer, 比如調用一些其他方法
public void ifPresent(Consumer<? super T> consumer);例如如下用法:
public void testIfPresent() {// ifPresent 表示 Optional 中的對象存在才會執行 Consumer 接口對象中的方法List<String> dataList = new ArrayList<>();// 1. 不為空沒有值的集合Optional.ofNullable(dataList).ifPresent(t -> {System.out.println("1"); // 輸出 1t.forEach(a -> System.out.println(a));}); }?
2.3.7?filter()
filter 方法用于過濾不符合條件的值, 接收一個 Predicate 參數, 如果符合條件返回代表值的 Optional 實例, 否則返回 empty;例如:
private static List<User> userList = new ArrayList<>();/*** 初始化 user 集合*/ @Before public void initEveryTestBefore() {userList.add(new User(22, "王旭", "wang.xu","123456", '1', true));userList.add(new User(21, "孫萍", "sun.ping","a123456", '2', false));userList.add(new User(23, "步傳宇", "bu.zhuan.yu", "b123456", '1', false));userList.add(new User(18, "蔡明浩", "cai.ming.hao","c123456", '1', true));userList.add(new User(17, "郭林杰", "guo.lin.jie", "d123456", '1', false));userList.add(new User(29, "韓凱", "han.kai", "e123456", '1', true));userList.add(new User(22, "韓天琪", "han.tian.qi","f123456", '2', false));userList.add(new User(21, "郝瑋", "hao.wei","g123456", '2', false));userList.add(new User(19, "胡亞強", "hu.ya.qing","h123456", '1', false));userList.add(new User(14, "季愷", "ji.kai","i123456", '1', false));userList.add(new User(17, "荊帥", "jing.shuai","j123456", '1', true));userList.add(new User(16, "姜有琪", "jiang.you.qi","k123456", '1', false));logger.info("initEveryTestBefore, size {}", userList.size()); }// filter: optional 中的對象在不為空,并且滿足某個條件的時候才會返回 @Test public void testFilter() {// 1. 在集合中有年齡大于 18 歲的才會返回所有對象Optional.ofNullable(userList).filter(t -> t.stream().anyMatch(u -> u.getAge() > 18)).ifPresent(t -> {t.forEach(u -> {System.out.println("1:" + u.toString());});});// 2. 因為集合中沒有年齡大于 50 歲的,因此不會返回任何對象Optional.ofNullable(userList).filter(t -> t.stream().anyMatch(u -> u.getAge() > 50)).ifPresent(t -> {t.forEach(u -> {System.out.println("2:" + u.toString());});}); }?
2.3.8?map()
map 方法是鏈式調用避免空指針的核心方法, 當實例包含值時, 對值執行傳入的 Function 邏輯, 并返回一個代表結果值的新的 Optional 實例;也就是將 optional 中的對象轉成 其他對象,或者修改對象中的屬性;如:
// 1. 返回 optional 中的對象年齡在 18 歲以上的 Optional.ofNullable(userList).map(t -> {List<User> tempList = new ArrayList<>();t.forEach(u -> {if (u.getAge() > 18) {tempList.add(u);}});return tempList;}).ifPresent(t -> {t.forEach(u -> {System.out.println("1:" + u.toString());});});?
2.3.9?flatMap()
flatMap方法是將 optional 中的對象轉成 optional 對象,或者修改對象中的屬性;與map方法類似。不同之處在于:
Optional.ofNullable(userList).map(t -> {List<User> tempList = new ArrayList<>();t.forEach(u -> {if (u.getAge() > 18) {tempList.add(u);}});//不同之處return Optional.of(tempList);})?
2.3.10?orElse()
orElse方法是如果實例包含值, 那么返回這個值, 否則返回指定的默認值, 如null
public T orElse(T other);?
2.3.11?orElseGet()
orElseGet方法是如果實例包含值, 返回這個值;否則,它會執行作為參數傳入的?Supplier(供應者)?函數式接口,并將返回其執行結果。
public T orElseGet(Supplier<? extends T> other);orElse與orElseGet不同之處:
public void givenPresentValue_whenCompare_thenOk() {User user = new User("john@gmail.com", "1234");logger.info("Using orElse");User result = Optional.ofNullable(user).orElse(createNewUser());logger.info("Using orElseGet");User result2 = Optional.ofNullable(user).orElseGet(() -> createNewUser()); }輸出結果:
Using orElse Creating New User Using orElseGet?
2.3.12?orElseThrow()
如果實例不包含值, 調用傳入的 Supplier 參數, 生成一個異常實例并拋出.這個方法通常與全局異常處理器一起使用, 當參數或者其他情況獲取不到值時, 拋出自定義異常, 由異常處理器處理成通用返回結果, 返回給前端;如:
Optional.ofNullable(tempList).orElseThrow(() -> runtimeException).forEach(t -> System.out.println("2:" + t));3.總結
以上即為JDK1.8新增的Optional類的常用方法及場景記錄!舉例的代碼方面,比較零散,但主要是為了方便大家有一個功能使用和方法差別上的了解!(所以,不要在意那些雞零狗碎的代碼意義~~~~)
路漫漫,其修遠兮!歡迎大家提問、留言!!!
?
微信同款:https://mp.weixin.qq.com/s/jhJBI6uK6bvCBTUxnj-LUA
總結
以上是生活随笔為你收集整理的【Java 8系列】Java开发者的判空利器 -- Optional的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: uni-app框架
- 下一篇: 【动画消消乐 】HTML+CSS 吃豆豆