避免null使用
大多數(shù)語言都有一個特殊的關(guān)鍵字或者對象來表示一個對象引用的是“無”殖熟,在Java,它是null斑响。在Java 里菱属,null 是一個關(guān)鍵字,不是一個對象舰罚,所以對它調(diào)用任何方法都是非法的纽门。但是這對語言設(shè)計者來說是一件令人疑惑的選擇。為什么要在程序員希望返回一個對象的時候返回一個關(guān)鍵字呢营罢?
Scala的Option類型
為了讓所有東西都是對象的目標更加一致赏陵,也為了遵循函數(shù)式編程的習(xí)慣饼齿,Scala鼓勵你在變量和函數(shù)返回值可能不會引用任何值的時候使用Option類型。在沒有值的時候蝙搔,使用None缕溉,這是Option的一個子類篱竭。如果有值可以引用忘分,就使用Some來包含這個值拿愧。Some也是Option的子類敷存。
None被聲明為一個對象募舟,而不是一個類猖败,因為我們只需要它的一個實例领虹。這樣届宠,它多少有點像null關(guān)鍵字赐写,但它卻是一個實實在在的鸟蜡,有方法的對象。
應(yīng)用例子
Option類型的值通常作為Scala集合類型(List,Map等)操作的返回類型血淌。比如Map的get方法:
scala> val capitals = Map("France"->"Paris", "Japan"->"Tokyo", "China"->"Beijing")
capitals: scala.collection.immutable.Map[String,String] = Map(France -> Paris, Japan -> Tokyo, China -> Beijing)
scala> capitals get "France"
res0: Option[String] = Some(Paris)
scala> capitals get "North Pole"
res1: Option[String] = None
Option有兩個子類別矩欠,Some和None。當(dāng)程序回傳Some的時候悠夯,代表這個函式成功地給了你一個String癌淮,而你可以透過get()函數(shù)拿到那個String,如果程序返回的是None沦补,則代表沒有字符串可以給你乳蓄。
在返回None,也就是沒有String給你的時候夕膀,如果你還硬要調(diào)用get()來取得 String 的話虚倒,Scala一樣是會拋出一個NoSuchElementException異常給你的。
我們也可以選用另外一個方法产舞,getOrElse魂奥。這個方法在這個Option是Some的實例時返回對應(yīng)的值,而在是None的實例時返回傳入的參數(shù)易猫。換句話說耻煤,傳入getOrElse的參數(shù)實際上是默認返回值。
scala> capitals get "North Pole" get
warning: there was one feature warning; re-run with -feature for details
java.util.NoSuchElementException: None.get
at scala.None$.get(Option.scala:347)
at scala.None$.get(Option.scala:345)
... 33 elided
scala> capitals get "France" get
warning: there was one feature warning; re-run with -feature for details
res3: String = Paris
scala> (capitals get "North Pole") getOrElse "Oops"
res7: String = Oops
scala> capitals get "France" getOrElse "Oops"
res8: String = Paris
通過模式匹配分離可選值准颓,如果匹配的值是Some的話哈蝇,將Some里的值抽出賦給x變量:
def showCapital(x: Option[String]) = x match {
case Some(s) => s
case None => "?"
}
提示
Scala程序使用Option非常頻繁,在Java中使用null來表示空值攘已,代碼中很多地方都要添加null關(guān)鍵字檢測炮赦,不然很容易出現(xiàn)NullPointException。因此Java程序需要關(guān)心那些變量可能是null,而這些變量出現(xiàn)null的可能性很低样勃,但一但出現(xiàn)吠勘,很難查出為什么出現(xiàn)NullPointerException性芬。
Scala的Option類型可以避免這種情況,因此Scala應(yīng)用推薦使用Option類型來代表一些可選值看幼。使用Option類型批旺,讀者一眼就可以看出這種類型的值可能為None。
實際上诵姜,多虧Scala的靜態(tài)類型汽煮,你并不能錯誤地嘗試在一個可能為null的值上調(diào)用方法。雖然在Java中這是個很容易犯的錯誤棚唆,它在Scala卻通不過編譯暇赤,這是因為Java中沒有檢查變量是否為null的編程作為變成Scala中的類型錯誤(不能將Option[String]當(dāng)做String來使用)。所以鞋囊,Option的使用極強地鼓勵了更加彈性的編程習(xí)慣。
詳解Option[T]
在Scala里Option[T]實際上是一個容器乘寒,就像數(shù)組或是List一樣,你可以把他看成是一個可能有零到一個元素的List。
當(dāng)你的Option里面有東西的時候,這個List的長度是1(也就是 Some)殿怜,而當(dāng)你的Option里沒有東西的時候鸠澈,它的長度是0(也就是 None)涵妥。
for循環(huán)
如果我們把Option當(dāng)成一般的List來用,并且用一個for循環(huán)來走訪這個Option的時候,如果Option是None,那這個for循環(huán)里的程序代碼自然不會執(zhí)行徙菠,于是我們就達到了「不用檢查Option是否為None這件事缺狠。
scala> val map1 = Map("key1" -> "value1")
map1: scala.collection.immutable.Map[String,String] = Map(key1 -> value1)
scala> val value1 = map1.get("key1")
value1: Option[String] = Some(value1)
scala> val value2 = map1.get("key2")
value2: Option[String] = None
scala> def printContentLength(x: Option[String]) {
| for (c <- x){
| println(c.length)
| }
| }
printContentLength: (x: Option[String])Unit
scala> printContentLength(value1)
6
scala> printContentLength(value2)
map操作
在函數(shù)式編程中有一個核心的概念之一是轉(zhuǎn)換穷劈,所以大部份支持函數(shù)式編程語言逼龟,都支持一種叫map()的動作宜肉,這個動作是可以幫你把某個容器的內(nèi)容日杈,套上一些動作之后啰劲,變成另一個新的容器。
現(xiàn)在我們考慮如何用Option的map方法實現(xiàn)length: xxx
的輸出形式:
先算出 Option 容器內(nèi)字符串的長度
然后在長度前面加上 "length: " 字樣
最后把容器走訪一次藕甩,印出容器內(nèi)的東西
scala> value1.map(_.length).map("length: " + _).foreach(println)
length: 6
scala> value1.map("length: " + _.length).foreach(println)
length: 6
透過這樣「轉(zhuǎn)換」的方法腋妙,我們一樣可以達成想要的效果痕檬,而且同樣不用去做「是否為 None」的判斷。
轉(zhuǎn)載請注明作者Jason Ding及其出處
GitCafe博客主頁(http://jasonding1354.gitcafe.io/)
Github博客主頁(http://jasonding1354.github.io/)
CSDN博客(http://blog.csdn.net/jasonding1354)
簡書主頁(http://www.reibang.com/users/2bd9b48f6ea8/latest_articles)
Google搜索jasonding1354進入我的博客主頁