Scala中優(yōu)雅的處理Null問題
前言
如果在scala代碼還在使用ids!=null,可能會被有的人嘲笑,都什么年代了,竟然還有這樣的寫法,NullPointerException見少了吧?
不過,據(jù)統(tǒng)計(jì):
Spark 源代碼使用了 821 次 Option 關(guān)鍵字,但它也直接在像if (ids != null)。
Spark 采用混合方式,大部分情況下使用 Option,但個(gè)別時(shí)候出于性能(這里主要是為了給使用這返回提示信息)原因才使用了null。
一個(gè)很好的習(xí)慣是當(dāng)有方法返回值可能為null的時(shí)候,使用Option來代替。
什么是Option
標(biāo)準(zhǔn)類庫中Option類型用樣例類來表示那種可能存在、也可能不存在的值。
Option的有兩個(gè)子類,Some 和 None,Some包裝了某個(gè)值,如Some(“Jack”),而None表示沒有值。
有的小伙伴看到依然云里霧里的,因此直接上一個(gè)簡單的例子,來感受一下這個(gè)Option究竟是什么東西:
煮個(gè)栗子
這里寫了一個(gè)把字符串轉(zhuǎn)為數(shù)值的方法,輸入的是字符串,輸出的這里注意一下,并不是直接輸出Int,而且一個(gè)泛型為Int的Option
def toInt(in: String): Option[Int] = { try { Some(Integer.parseInt(in.trim)) } catch { case e: NumberFormatException => None } }
如何使用這個(gè)函數(shù):
toInt("s") match { case Some(i) => println(i) case None => println("您輸入的字符串無法轉(zhuǎn)為數(shù)字") }
簡單的總結(jié)一下這個(gè)Option的使用,其實(shí)就是把你原本要返回的類型,直接返回泛型為該類型的Option,然后寫正常返回值的時(shí)候返回Some,異常的時(shí)候返回None即可。而調(diào)用方法的時(shí)候,需要用到match case分別做處理。
有人看到這里可能會抱怨:一個(gè)簡單的null判斷,寫了這么一大堆,還不如java中的直接用i!=null來判斷簡單粗暴呢。
但是如果這個(gè)toInt函數(shù)是別人寫的,你是個(gè)使用者,你一定會遇到如下問題:
- 你沒有讀API文檔,根本不知道toInt可能會返回一個(gè)null,并且可能你寫的代碼會拋出NullPointerException
- 你讀了API文檔,并且也有很多使用這個(gè)函數(shù)的經(jīng)驗(yàn),知道它可能會返回null,因此你肯定會寫如下代碼來處理可能出現(xiàn)的空指針異常
Integer i = toInt(someString); if (i == null) { System.out.println("您輸入的字符串無法轉(zhuǎn)為數(shù)字"); } else { System.out.println(i); }
該代碼并不比 Scala的Option和match方法差,但你確實(shí)必須閱讀API文檔才能知道必須得這樣處理。
3.當(dāng)然還可以通過拋出NumberFormatException來處理null或者其他一些異常情況
Option的好處不僅如此
比如想統(tǒng)計(jì)下面list中的總和,這些字符串有的可以轉(zhuǎn)為Int,有的不可以
val bag = List("1", "2", "foo", "3", "bar")
要實(shí)現(xiàn)這個(gè)需求,感覺要寫很多代碼才能實(shí)現(xiàn),其實(shí)在scala中只要一行代碼就可以實(shí)現(xiàn):
val sum = bag.flatMap(toInt).sum
為什么可以這么簡單的就實(shí)現(xiàn):
我們的toInt方法返回的是Some[Int]或None,而flatMap知道如何處理這些值,所以實(shí)現(xiàn)起來就小菜一碟了,而且還很容易閱讀和理解。
這就是使用Option/Some/None 模式真正牛叉的地方了
簡單的總結(jié)java null 與 scala Option
如果你用別人寫的java方法,那么一般需要閱讀API文檔,或者是使用后拋出了NullPointException后,查文檔和資料發(fā)現(xiàn),需要處理這個(gè)空指針異常。那么scala Option我們在使用函數(shù)的時(shí)候,可以看到返回值是個(gè)Option[Int](直接在IDE中就能看到返回值類型,不需要去閱讀該函數(shù)的API文檔),說明開發(fā)這個(gè)函數(shù)的人一定用了Option/Some/None這一套組合拳,因此就知道用match case來解決了。
在我看來,其實(shí)從寫代碼的角度來看代碼量并沒有減少,優(yōu)點(diǎn)有兩:
- 更具有可讀性,
- 避免在使用函數(shù)的時(shí)候,出現(xiàn)空指針異常 Option的缺點(diǎn)
Option的缺點(diǎn)
有的人會說,你前言中都說了連spark源碼都不是一律采用Option來處理null值,你既然上面吹的神乎其神的,人家干嘛不全部用Option?
這里就得說一下Option的一個(gè)缺點(diǎn):
無法說出某件事失敗的原因(也就是為什么你得到了一個(gè)None而不是一個(gè)Some),因?yàn)楦揪涂床坏藉e誤異常信息
對此scala 的解決方案是使用Either Left 和 Right來處理異常信息
Either Left Right簡介與使用
Either其實(shí)用起來和Option很像,不同的是Either可以返回一個(gè)字符串來描述發(fā)生的問題。
Either 和Option的比較:
Either 就像 Option
Right 就像 Some
Left 就像 None (不過它是寫出發(fā)生問題的原因)
還是搞一段代碼來解釋
/** * 這里寫個(gè)簡單的方法來演示,如何寫一個(gè)返回值為Either的方法 * 以及Either中Left和Right的用法 */ def divideXByY(x: Int, y: Int): Either[String, Int] = { if (y == 0) Left("零不能作為除數(shù)") else Right(x / y) } // 使用 Either, Left, and Right的幾種不同方式 println(divideXByY(1, 0)) println(divideXByY(1, 1)) divideXByY(1, 0) match { case Left(s) => println("Answer: " + s) case Right(i) => println("Answer: " + i) }
上面divideXByY方法返回的是一個(gè)Either[String, Int],這個(gè)泛型String就是Left方法中傳入的數(shù)據(jù)類型,而Int就是Right方法傳入的數(shù)據(jù)類型。
通過上面的例子,會發(fā)現(xiàn)這一套東西和Option/Some/None使用起來很像,唯一不同的是,Either將出錯的信息可以傳回,使用者可以看到異常信息。我們看看官網(wǎng)是怎么說的:
Represents a value of one of two possible types (a disjoint union.) Instances of Either are either an instance of Left or Right.
A common use of Either is as an alternative to Option for dealing with possible missing values. In this usage,
scala.None is replaced with a Left which can contain useful information. Right takes the place of Some.
Convention dictates that Left is used for failure and Right is used for success.
說白了就是我上面寫的,如果要返回錯誤信息給使用者,就用Either。不過根據(jù)我經(jīng)驗(yàn),一般這樣的場景不算多。因此最長用的還是Option。
到此這篇關(guān)于Scala中如何優(yōu)雅的處理Null的文章就介紹到這了,更多相關(guān)Scala處理Null內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
在service層注入mapper時(shí)報(bào)空指針的解決
這篇文章主要介紹了在service層注入mapper時(shí)報(bào)空指針的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06Spring中@EnableScheduling注解的工作原理詳解
這篇文章主要介紹了Spring中@EnableScheduling注解的工作原理詳解,@EnableScheduling是 Spring Framework 提供的一個(gè)注解,用于啟用Spring的定時(shí)任務(wù)(Scheduling)功能,需要的朋友可以參考下2024-01-01SpringBoot如何動態(tài)改變?nèi)罩炯墑e
這篇文章主要介紹了SpringBoot如何動態(tài)改變?nèi)罩炯墑e,幫助大家更好的理解和使用springboot框架,感興趣的朋友可以了解下2020-12-12SpringMVC Json自定義序列化和反序列化的操作方法
這篇文章主要介紹了SpringMVC Json自定義序列化和反序列化的操作方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01SpringMVC框架如何與Junit整合看這個(gè)就夠了
這篇文章主要介紹了SpringMVC框架如何與Junit整合看這個(gè)就夠了,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05