Option 型態


當你呼叫了某個函式或執行了某個物件方法,函式或方法可能有傳回物件,也有可能沒有結果,如果沒有結果時,Scala不建議傳回null。

來看看一個例子,若你設計了一個doSomeUpper()函式:
def doSomeUpper(x: String) = {
if(x == "some") x.toUpperCase else null
}

有人可能會使用你的這個函式,如果他有查看過你提供的API文件,或者知道函式有可能傳回null,那他應該這麼使用這個函式:
val result = doSomeUpper("other")
if(result != null) println(result(0)) else println("...XD")

如果他忘了要對傳回結果作null檢查,那麼就會有可能發生NullPointerException:
val result = doSomeUpper("other")
println(result(0))

讓函式或方法傳回null,對於使用該函式或方法的客戶端,沒有一個方式提醒或強制他們處理可能傳回的null,因而在程式中有可能發生NullPointerException。

對於一些有可能傳回也可能不傳回物件的函式或方法,Scala建議你傳回 scala.Option。Option有兩個子類別:scala.Somescala.None。有傳回物件時,可以使用Some包裹傳回物件,沒有傳回物件時,可直接傳回None。你可以這麼改寫先前的函式:
def doSomeUpper(x: String) = {  // 傳回型態為Option[String]
if(x == "some") Some(x.toUpperCase) else None
}

這個函式一定會傳回物件,型態為Option[String](因為Scala類型推斷的特性,所以沒有在定義函式時標示出傳回型態),要不就是Some,要不就是None。那麼要如何使用呢?
val result = doSomeUpper("other")
println(result.getOrElse("...XD"))

Option有個getOrElse()方法,如果傳回結果包裹在Some中,則getOrElse()會取出包裹在當中的物件,如果傳回None,則取得你指定給 getOrElse()的物件,藉由傳回Option物件,可以避免使用方法或函式的客戶端,因為沒有處理null的情況,而有發生 NullPointerException的可能性。

Scala中群集物件的一些方法傳回值就是Option型態,例如Map的get()方法:
val rooms = Map(101 -> "Justin", 102 -> "caterpillar")
// get() 傳回型態是 Option[String]
println(rooms.get(101).getOrElse("empty")) // Justin
println(rooms.get(102).getOrElse("empty")) // caterpillar
println(rooms.get(103).getOrElse("empty")) // empty

Some與None在定義時,都使用了case關鍵字修飾,所以上例可以改用模式比對的方式來處理傳回結果:
def name(o: Option[String]) = o match {
case Some(r) => r
case None => "empty"
}

val rooms = Map(101 -> "Justin", 102 -> "caterpillar")
println(name(rooms.get(101))) // Justin
println(name(rooms.get(102))) // caterpillar
println(name(rooms.get(103))) // empty

如果你有一個List中包括了數個函式的執行結果,而每個執行結果是以Option來傳回,也就是有結果時,用Some包裹,沒有結果時是None,則在想要取得結果時,就可以如下方便地結合for迴圈:
val list = List(Some("Justin"), None, Some("momor"))
for(Some(result) <- list) {
println(result)
}