最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java函數(shù)式開發(fā) Optional空指針處理

 更新時間:2016年09月04日 17:16:39   作者:隨風溜達的向日葵  
本文主要介紹Java函數(shù)式開發(fā) Optional空指針處理,這里整理了相關資料,及示例代碼,有興趣的小伙伴可以參考下

摘要

空閑時會抽空學習同在jvm上運行的Groovy和Scala,發(fā)現(xiàn)他們對null的處理比早期版本Java慎重很多。在Java8中,Optional為函數(shù)式編程的null處理給出了非常優(yōu)雅的解決方案。本文將說明長久以來Java中對null的蹩腳處理,然后介紹使用Optional來實現(xiàn)Java函數(shù)式編程。

那些年困擾著我們的null

在Java江湖流傳著這樣一個傳說:直到真正了解了空指針異常,才能算一名合格的Java開發(fā)人員。在我們逼格閃閃的java碼字符生涯中,每天都會遇到各種null的處理,像下面這樣的代碼可能我們每天都在反復編寫:

if(null != obj1){
 if(null != obje2){
   // do something
 }
}

稍微有點眼界javaer就去干一些稍有逼格的事,弄一個判斷null的方法:

boolean checkNotNull(Object obj){
 return null == obj ? false : true; 
}

void do(){
 if(checkNotNull(obj1)){
   if(checkNotNull(obj2)){
    //do something
   }
 }
}

然后,問題又來了:如果一個null表示一個空字符串,那”"表示什么?

然后慣性思維告訴我們,”"和null不都是空字符串碼?索性就把判斷空值升級了一下:

boolean checkNotBlank(Object obj){
 return null != obj && !"".equals(obj) ? true : false; 
}
void do(){
 if(checkNotBlank(obj1)){
   if(checkNotNull(obj2)){
    //do something
   }
 }
}

有空的話各位可以看看目前項目中或者自己過往的代碼,到底寫了多少和上面類似的代碼。

不知道你是否認真思考過一個問題:一個null到底意味著什么?

  1. 淺顯的認識——null當然表示“值不存在”。
  2. 對內存管理有點經(jīng)驗的理解——null表示內存沒有被分配,指針指向了一個空地址。
  3. 稍微透徹點的認識——null可能表示某個地方處理有問題了,也可能表示某個值不存在。
  4. 被虐千萬次的認識——哎喲,又一個NullPointerException異常,看來我得加一個if(null != value)了。

回憶一下,在咱們前面碼字生涯中到底遇到過多少次java.lang.NullPointerException異常?NullPointerException作為一個RuntimeException級別的異常不用顯示捕獲,若不小心處理我們經(jīng)常會在生產(chǎn)日志中看到各種由NullPointerException引起的異常堆棧輸出。而且根據(jù)這個異常堆棧信息我們根本無法定位到導致問題的原因,因為并不是拋出NullPointerException的地方引發(fā)了這個問題。我們得更深處去查詢什么地方產(chǎn)生了這個null,而這個時候日志往往無法跟蹤。

有時更悲劇的是,產(chǎn)生null值的地方往往不在我們自己的項目代碼中。這就存在一個更尷尬的事實——在我們調用各種良莠不齊第三方接口時,說不清某個接口在某種機緣巧合的情況下就會返回一個null……

回到前面對null的認知問題。很多javaer認為null就是表示“什么都沒有”或者“值不存在”。按照這個慣性思維我們的代碼邏輯就是:你調用我的接口,按照你給我的參數(shù)返回對應的“值”,如果這條件沒法找到對應的“值”,那我當然返回一個null給你表示沒有“任何東西”了。我們看看下面這個代碼,用很傳統(tǒng)很標準的Java編碼風格編寫:

class MyEntity{
  int id;
  String name;
  String getName(){
   return name;
  }
}

// main
public class Test{
  public static void main(String[] args) 
    final MyEntity myEntity = getMyEntity(false);
    System.out.println(myEntity.getName());
  }

  private getMyEntity(boolean isSuc){
    if(isSuc){
      return new MyEntity();
    }else{
      return null;
    }
  }
}

這一段代碼很簡單,日常的業(yè)務代碼肯定比這個復雜的多,但是實際上我們大量的Java編碼都是按這種套路編寫的,懂貨的人一眼就可以看出最終肯定會拋出NullPointerException。但是在我們編寫業(yè)務代碼時,很少會想到要處理這個可能會出現(xiàn)的null(也許API文檔已經(jīng)寫得很清楚在某些情況下會返回null,但是你確保你會認真看完API文檔后才開始寫代碼么?),直到我們到了某個測試階段,突然蹦出一個NullPointerException異常,我們才意識到原來我們得像下面這樣加一個判斷來搞定這個可能會返回的null值。

// main
public class Test{
  public static void main(String[] args) 
    final MyEntity myEntity = getMyEntity(false);
    if(null != myEntity){
      System.out.println(myEntity.getName());
    }else{
      System.out.println("ERROR");
    }
  }
}

仔細想想過去這么些年,咱們是不是都這樣干過來的?如果直到測試階段才能發(fā)現(xiàn)某些null導致的問題,那么現(xiàn)在問題就來了——在那些雍容繁雜、層次分明的業(yè)務代碼中到底還有多少null沒有被正確處理呢?

對于null的處理態(tài)度,往往可以看出一個項目的成熟和嚴謹程度。比如Guava早在JDK1.6之前就給出了優(yōu)雅的null處理方式,可見功底之深。

鬼魅一般的null阻礙我們進步

如果你是一位聚焦于傳統(tǒng)面向對象開發(fā)的Javaer,或許你已經(jīng)習慣了null帶來的種種問題。但是早在許多年前,大神就說了null這玩意就是個坑。

托尼.霍爾(你不知道這貨是誰嗎?自己去查查吧)曾經(jīng)說過:“I call it my billion-dollar mistake. It was the invention of the null reference in 1965. I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement.”(大意是:“哥將發(fā)明null這事稱為價值連城的錯誤。因為在1965那個計算機的蠻荒時代,空引用太容易實現(xiàn),讓哥根本經(jīng)不住誘惑發(fā)明了空指針這玩意?!保?。

然后,我們再看看null還會引入什么問題。

看看下面這個代碼:

String address = person.getCountry().getProvince().getCity();

如果你玩過一些函數(shù)式語言(Haskell、Erlang、Clojure、Scala等等),上面這樣是一種很自然的寫法。用Java當然也可以實現(xiàn)上面這樣的編寫方式。

但是為了完滿的處理所有可能出現(xiàn)的null異常,我們不得不把這種優(yōu)雅的函數(shù)編程范式改為這樣:

if (person != null) {
 Country country = person.getCountry();
 if (country != null) {
 Province province = country.getProvince();
 if (province != null) {
  address = province.getCity();
 }
 }
}

瞬間,高逼格的函數(shù)式編程Java8又回到了10年前。這樣一層一層的嵌套判斷,增加代碼量和不優(yōu)雅還是小事。更可能出現(xiàn)的情況是:在大部分時間里,人們會忘記去判斷這可能會出現(xiàn)的null,即使是寫了多年代碼的老人家也不例外。

上面這一段層層嵌套的 null 處理,也是傳統(tǒng)Java長期被詬病的地方。如果以Java早期版本作為你的啟蒙語言,這種get->if null->return 的臭毛病會影響你很長的時間(記得在某國外社區(qū),這被稱為:面向entity開發(fā))。

利用Optional實現(xiàn)Java函數(shù)式編程

好了,說了各種各樣的毛病,然后我們可以進入新時代了。

早在推出Java SE 8版本之前,其他類似的函數(shù)式開發(fā)語言早就有自己的各種解決方案。下面是Groovy的代碼:

String version = computer?.getSoundcard()?.getUSB()?.getVersion():"unkonwn";

Haskell用一個 Maybe 類型類標識處理null值。而號稱多范式開發(fā)語言的Scala則提供了一個和Maybe差不多意思的Option[T],用來包裹處理null。

Java8引入了 java.util.Optional<T>來處理函數(shù)式編程的null問題,Optional<T>的處理思路和Haskell、Scala類似,但又有些許區(qū)別。先看看下面這個Java代碼的例子:

public class Test {
 public static void main(String[] args) {
 final String text = "Hallo world!";
 Optional.ofNullable(text)//顯示創(chuàng)建一個Optional殼
   .map(Test::print)
  .map(Test::print)
  .ifPresent(System.out::println);

 Optional.ofNullable(text)
  .map(s ->{ 
  System.out.println(s);
  return s.substring(6);
  })
  .map(s -> null)//返回 null
  .ifPresent(System.out::println);
 }
 // 打印并截取str[5]之后的字符串
 private static String print(String str) {
 System.out.println(str);
 return str.substring(6);
 }
}
//Consol 輸出
//num1:Hallo world!
//num2:world!
//num3:
//num4:Hallo world!

 (可以把上面的代碼copy到你的IDE中運行,前提是必須安裝了JDK8。)

上面的代碼中創(chuàng)建了2個Optional,實現(xiàn)的功能基本相同,都是使用Optional作為String的外殼對String進行截斷處理。當在處理過程中遇到null值時,就不再繼續(xù)處理。我們可以發(fā)現(xiàn)第二個Optional中出現(xiàn)s->null之后,后續(xù)的ifPresent不再執(zhí)行。

注意觀察輸出的 //num3:,這表示輸出了一個”"字符,而不是一個null。

Optional提供了豐富的接口來處理各種情況,比如可以將代碼修改為:

public class Test {
 public static void main(String[] args) {
 final String text = "Hallo World!";
 System.out.println(lowerCase(text));//方法一
 lowerCase(null, System.out::println);//方法二
 }

 private static String lowerCase(String str) {
 return Optional.ofNullable(str).map(s -> s.toLowerCase()).map(s->s.replace("world", "java")).orElse("NaN");
 }

 private static void lowerCase(String str, Consumer<String> consumer) {
 consumer.accept(lowerCase(str));
 }
}
//輸出
//hallo java!
//NaN

這樣,我們可以動態(tài)的處理一個字符串,如果在任何時候發(fā)現(xiàn)值為null,則使用orElse返回預設默認的“NaN”。

總的來說,我們可以將任何數(shù)據(jù)結構用Optional包裹起來,然后使用函數(shù)式的方式對他進行處理,而不必關心隨時可能會出現(xiàn)的null。

我們看看前面提到的Person.getCountry().getProvince().getCity()怎么不用一堆if來處理。

第一種方法是不改變以前的entity:

import java.util.Optional;
public class Test {
 public static void main(String[] args) {
 System.out.println(Optional.ofNullable(new Person())
  .map(x->x.country)
  .map(x->x.provinec)
  .map(x->x.city)
  .map(x->x.name)
  .orElse("unkonwn"));
 }
}
class Person {
 Country country;
}
class Country {
 Province provinec;
}
class Province {
 City city;
}
class City {
 String name;
}

這里用Optional作為每一次返回的外殼,如果有某個位置返回了null,則會直接得到”unkonwn”。

第二種辦法是將所有的值都用Optional來定義:

import java.util.Optional;
public class Test {
 public static void main(String[] args) {
 System.out.println(new Person()
  .country.flatMap(x -> x.provinec)
  .flatMap(Province::getCity)
  .flatMap(x -> x.name)
  .orElse("unkonwn"));
 }
}
class Person {
 Optional<Country> country = Optional.empty();
}
class Country {
 Optional<Province> provinec;
}
class Province {
 Optional<City> city;
 Optional<City> getCity(){//用于::
 return city;
 }
}
class City {
 Optional<String> name;
}

第一種方法可以平滑的和已有的JavaBean、Entity或POJA整合,而無需改動什么,也能更輕松的整合到第三方接口中(例如spring的bean)。建議目前還是以第一種Optional的使用方法為主,畢竟不是團隊中每一個人都能理解每個get/set帶著一個Optional的用意。

Optional還提供了一個filter方法用于過濾數(shù)據(jù)(實際上Java8里stream風格的接口都提供了filter方法)。例如過去我們判斷值存在并作出相應的處理:

if(Province!= null){
 City city = Province.getCity();
 if(null != city && "guangzhou".equals(city.getName()){
  System.out.println(city.getName());
 }else{
  System.out.println("unkonwn");
 }
}

現(xiàn)在我們可以修改為

Optional.ofNullable(province)
  .map(x->x.city)
  .filter(x->"guangzhou".equals(x.getName()))
  .map(x->x.name)
  .orElse("unkonw");

到此,利用Optional來進行函數(shù)式編程介紹完畢。Optional除了上面提到的方法,還有orElseGet、orElseThrow等根據(jù)更多需要提供的方法。orElseGet會因為出現(xiàn)null值拋出空指針異常,而orElseThrow會在出現(xiàn)null時,拋出一個使用者自定義的異常??梢圆榭碅PI文檔來了解所有方法的細節(jié)。

寫在最后的

Optional只是Java函數(shù)式編程的冰山一角,需要結合lambda、stream、Funcationinterface等特性才能真正的了解Java8函數(shù)式編程的效用。本來還想介紹一些Optional的源碼和運行原理的,但是Optional本身的代碼就很少、API接口也不多,仔細想想也沒什么好說的就省略了。

Optional雖然優(yōu)雅,但是個人感覺有一些效率問題,不過還沒去驗證。如果有誰有確實的數(shù)據(jù),請告訴我。

本人也不是“函數(shù)式編程支持者”。從團隊管理者的角度來說,每提升一點學習難度,人員的使用成本和團隊交互成本就會更高一些。就像在傳說中Lisp可以比C++的代碼量少三十倍、開發(fā)更高效,但是若一個國內的常規(guī)IT公司真用Lisp來做項目,請問去哪、得花多少錢弄到這些用Lisp的哥們???

但是我非常鼓勵大家都學習和了解函數(shù)式編程的思路。尤其是過去只侵淫在Java這一門語言、到現(xiàn)在還不清楚Java8會帶來什么改變的開發(fā)人員,Java8是一個良好的契機。更鼓勵把新的Java8特性引入到目前的項目中,一個長期配合的團隊以及一門古老的編程語言都需要不斷的注入新活力,否則不進則退。

以上就是對Java Optional 的資料整理,后續(xù)繼續(xù)補充相關資料,謝謝大家對本站的支持!

相關文章

  • Java通過Freemarker模板實現(xiàn)生成Word文件

    Java通過Freemarker模板實現(xiàn)生成Word文件

    FreeMarker是一款模板引擎: 即一種基于模板和要改變的數(shù)據(jù), 并用來生成輸出文本的通用工具。本文將根據(jù)Freemarker模板實現(xiàn)生成Word文件,需要的可以參考一下
    2022-09-09
  • Springboot敏感字段脫敏的實現(xiàn)思路

    Springboot敏感字段脫敏的實現(xiàn)思路

    這篇文章主要介紹了Springboot敏感字段脫敏的實現(xiàn)思路,本文通過實例代碼給大家介紹的非常詳細,需要的朋友可以參考下
    2021-09-09
  • Java數(shù)組擴容實例代碼

    Java數(shù)組擴容實例代碼

    這篇文章主要介紹了Java數(shù)組擴容實例代碼,具有一定借鑒價值,需要的朋友可以參考下
    2017-11-11
  • 詳解Java中Optional類的使用方法

    詳解Java中Optional類的使用方法

    Optional的作用是什么?他都有哪些方法?阿里規(guī)范點名說盡量用Optional來避免空指針,那么什么場景用Optional?本篇文章圍繞這三點來進行講解,感興趣的可以學習一下
    2022-05-05
  • Java?電話號碼的組合示例詳解

    Java?電話號碼的組合示例詳解

    這篇文章主要介紹了Java?電話號碼的組合,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-03-03
  • 同時使用@LoadBalanced?@RefreshScope注解負載均衡失效分析

    同時使用@LoadBalanced?@RefreshScope注解負載均衡失效分析

    這篇文章主要為大家介紹了同時使用@LoadBalanced?@RefreshScope負載均衡失效問題分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-02-02
  • 百度Java面試題 前200頁精選(中)

    百度Java面試題 前200頁精選(中)

    這篇文章主要為大家分享了Java面試資源中篇,百度“Java面試題”前200頁都在這里了,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-12-12
  • Springboot實現(xiàn)多數(shù)據(jù)源切換詳情

    Springboot實現(xiàn)多數(shù)據(jù)源切換詳情

    這篇文章主要介紹了Springboot實現(xiàn)多數(shù)據(jù)源切換詳情,文章圍繞主題展開詳細的內容介紹,具有一定的參考價值,感興趣的朋友可以參考一下
    2022-09-09
  • 全面解析JPA?倉庫repository中的findAll()方法

    全面解析JPA?倉庫repository中的findAll()方法

    這篇文章主要介紹了全面解析JPA?倉庫repository中的findAll()方法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • Java基礎之數(shù)組模擬循環(huán)隊列

    Java基礎之數(shù)組模擬循環(huán)隊列

    這篇文章主要介紹了Java基礎之數(shù)組模擬循環(huán)隊列,文中有非常詳細的代碼示例,對正在學習java基礎的小伙伴們也有非常好的幫助,需要的朋友可以參考下
    2021-04-04

最新評論

虎林市| 阿坝县| 嘉义市| 泸西县| 醴陵市| 东兰县| 黔江区| 沙湾县| 深水埗区| 长乐市| 阳新县| 绥滨县| 南丹县| 绥芬河市| 陵水| 沙洋县| 闻喜县| 南召县| 永年县| 政和县| 柳江县| 石泉县| 青神县| 丰顺县| 南乐县| 临江市| 嘉峪关市| 多伦县| 保德县| 西吉县| 泾川县| 贞丰县| 金门县| 遵义市| 鄄城县| 临邑县| 盐山县| 林周县| 瑞昌市| 古蔺县| 吴旗县|