前情提要
Scala函數(shù)式編程指南(一) 函數(shù)式思想介紹
scala函數(shù)式編程(二) scala基礎(chǔ)語法介紹
Scala函數(shù)式編程(三) scala集合和函數(shù)
Scala函數(shù)式編程(四)函數(shù)式的數(shù)據(jù)結(jié)構(gòu) 上
Scala函數(shù)式編程(四)函數(shù)式的數(shù)據(jù)結(jié)構(gòu) 下
1.面向?qū)ο蟮腻e誤處理
在介紹scala的函數(shù)式的錯誤處理之前祖凫,我們要先來介紹一下其他情況下的錯誤處理方式厨剪。
以java為例,常見的錯誤處理方式不外乎兩種扬跋,一種是及時捕捉到異常拌消,然后當(dāng)場進(jìn)行處理。
try{
...
}catch(Exception e){
...
}finally{
}
另一種則是將異常拋出,層層捕獲埃跷,然后在最上層對異常進(jìn)行統(tǒng)一處理,這種通常是在大型項目的時候會使用邮利。
這兩種錯誤處理的方法是弥雹,在我們?nèi)粘5木幊讨校呀?jīng)足以應(yīng)對多種情況延届。
但在函數(shù)式編程中卻不行剪勿,函數(shù)式編程追求的是無副作用的代碼,無副作用最直接的應(yīng)用就是可以放心得并發(fā)運(yùn)行方庭,而拋出異常卻會產(chǎn)生副作用厕吉。
try catch處理的弊端,在并發(fā)編程中其實有較為明顯的體現(xiàn)械念。
以spark為例头朱,如果spark主節(jié)點master詢問worker節(jié)點的健康情況,當(dāng)worker節(jié)點出現(xiàn)異常時龄减,顯然讓master節(jié)點來捕獲并處理這個異常项钮,有點不符合情理。
更合理的處理希停,應(yīng)該是讓master接收到一個表示錯誤情況的消息烁巫,然后再決定接下來如何處理。而worker的異常就讓worker自己去解決吧宠能。
而在scala中程拭,有一種特定的類型,它用來表示可能導(dǎo)致異常的一個計算過程棍潘,這就是Try恃鞋。
2.從Option到Try
前面有介紹過Option,相關(guān)介紹可以看這里Scala函數(shù)式編程(三) scala集合和函數(shù)亦歉。
這里簡單介紹一下Option恤浪。
Option呢,其實就是薛定諤的值肴楷,里面可能有值水由,也可能沒有值。只有到要看的時候赛蔫,才會知道Option里面到底有沒有值砂客。
Option全程叫Option[A]泥张,表示Option里面存的是A類型的值,這個A可以是Int鞠值,String媚创,等等。我們可以通過get這個api來獲取Option[A]里面的值彤恶,當(dāng)不存在時钞钙,get會返回None。
可以通過isEmpty声离,來確認(rèn)Option里面到底是不是有值芒炼。也可以通過getOrElse來指定沒有值的時候要返回什么值。
Try[A]和Option類似术徊,都是表示一個可能有也可能沒有的東西本刽。實際對應(yīng)過來, Try[A]就表示一個可能成功也可以失敗的計算赠涮,如果成功盅安,則返回A類型,如果失敗世囊,則返回Throwable别瞭。
先最在交互式環(huán)境中直觀看一下怎么使用吧:
scala> import scala.util.Try
import scala.util.Try
scala> Try(1+1)
res15: scala.util.Try[Int] = Success(2)
scala> Try(1/0)
res16: scala.util.Try[Int] = Failure(java.lang.ArithmeticException: / by zero)
能夠?qū)崿F(xiàn)這個功能签钩,主要是因為Try的兩個子類型:
- Success[A]:代表成功的計算轩端。
- 封裝了 Throwable 的 Failure[A]:代表出了錯的計算。
是不是和Option很像呢喜德?也是薛定諤的錯誤嗤瞎,在沒打開來看之前墙歪,Try里面可能是成功的,也可能是失敗的贝奇。
同樣可以通過isSuccess和isFailure來確認(rèn)到底這個Try是成功還是失敗虹菲。
如果一個函數(shù)中有一個計算可能會出錯,那么我們就可以直讓函數(shù)返回Try掉瞳,然后對成功還是錯誤毕源,就全交由調(diào)用者來進(jìn)行處理,比如上面說到的陕习,Spark的那個例子霎褐。
3.Try的使用
上面初步介紹了Try的含義和用法,接下來就來看看Try這個東西该镣,還有哪些常規(guī)的用法吧冻璃。
3.1 map
map是scala里面非常常用的一種操作,Try里面也有!
對Try使用Map的話省艳,會將一個是Success[A]的Try[A]映射到Try[B]會得到Success[B]娘纷。如果它是Failure[A],就會得到Failure[B]跋炕,而且包含的異常和Failure[A]一樣赖晶。
看看例子吧:
//新建一個Try,注意枣购,這里是Try[Int]
scala> val tryMap = Try(1+1)
tryMap: scala.util.Try[Int] = Success(2)
//使用Map嬉探,讓它變成Try[String]了
scala> tryMap.map(_.toString)
res46: scala.util.Try[String] = Success(2)
//新建一個會失敗的Try[Int]
scala> val tryMapFail = Try(1 / 0)
tryMapFail: scala.util.Try[Int] = Failure(java.lang.ArithmeticException: / by zero)
//轉(zhuǎn)換成Try[String]了擦耀,但Failure的異常類型不變
scala> tryMapFail.map(_.toString)
res47: scala.util.Try[String] = Failure(java.lang.ArithmeticException: / by zero)
Try不止支持map棉圈,還支持for,flatMap眷蜓,filter等常規(guī)操作分瘾,從這個角度看,Try反而更像一種數(shù)據(jù)結(jié)構(gòu)吁系。
3.2 錯誤時候的默認(rèn)值getOrElse
和Option一樣德召,Try還很方便得提供了getOrElse這個方法。當(dāng)你想為失敗的時候做些什么的時候就可以用這個api汽纤。
這個我舉個簡單的例子上岗,將字符串轉(zhuǎn)換為Int類型。在字符串轉(zhuǎn)Int類型的時候呢蕴坪,可能會遇到一些不符合規(guī)范的數(shù)據(jù)肴掷。這時候你就不得不考慮數(shù)據(jù)是否可以安全得轉(zhuǎn)換成Int,但有了Try背传,可以很方便得用getOrElse呆瞻,方法。
當(dāng)遇到不能轉(zhuǎn)成Int的字符串径玖,給與一個默認(rèn)值即可痴脾。
scala> import scala.util.Try
import scala.util.Try
scala> "12".toInt
res17: Int = 12
scala> "asd".toInt
java.lang.NumberFormatException: For input string: "asd"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:580)
at java.lang.Integer.parseInt(Integer.java:615)
at scala.collection.immutable.StringLike$class.toInt(StringLike.scala:272)
at scala.collection.immutable.StringOps.toInt(StringOps.scala:29)
... 32 elided
scala> Try("asd".toInt).getOrElse(-1)
res19: Int = -1
但這里還是得多說一句,這種做法會忽略掉原本應(yīng)該拋出的錯誤梳星,你需要明確知道自己確實是要忽略掉這個錯誤才能這樣用赞赖。
否則可能因為設(shè)置的默認(rèn)值導(dǎo)致出現(xiàn)問題,而毫無頭緒冤灾,因為程序并沒有報任何錯誤!!
3.3 模式匹配
我們可以不必如java的try catch那般去處理Try失敗時返回的異常薯定。因為我們有scala的模式匹配。
不得不說瞳购,模式匹配真的是很強(qiáng)大的一個語言特性话侄。前面不是說到嘛,Try有兩個子類,Success和Failure年堆,成功時候返回Success吞杭,失敗時返回Failure。
所以我們就能夠這樣做:
import scala.util.Success
import scala.util.Failure
val operation = Try(1 / 0)
operation match {
case Success(num) => println(num)
case Failure(ex) => println(s"Problem is ${ex.getMessage}")
}
因為除數(shù)為0变丧,所以這個Try是失敗的芽狗,所以這里會輸出:Problem is / by zero
scala強(qiáng)大的模式匹配,可以方便得讓我們處理錯誤和非錯誤的情況痒蓬。
4. 小結(jié)
Scala 的錯誤處理和其他范式的編程語言有很大的不同童擎。 Try 類型可以讓你將可能會出錯的計算封裝在一個容器里,并優(yōu)雅的去處理計算得到的值攻晒。 并且可以像操作集合和 Option 那樣統(tǒng)一的去操作 Try顾复。
同時Try[A]也支持常見數(shù)據(jù)結(jié)構(gòu)中的操作,諸如Map鲁捏,F(xiàn)ilter等常規(guī)的api都支持芯砸。
Try這種錯誤處理的方式,明顯更適用于函數(shù)式的情況给梅,也就是說更適合在并發(fā)編程的時候使用假丧。
但在我看來,Try也是有一些不好的地方动羽,比如說在代碼可讀性方面就比try catch這種方式差包帚。不得不說,雖然寫起來比較啰嗦运吓,但看著這個結(jié)構(gòu)確實是一目了然渴邦。
但是不管如何,在我看來羽德,函數(shù)式的錯誤處理依舊是很有趣的一個東西几莽。如果合適的話,可以多在代碼中嘗試去使用:)
以上~