天天看點

Scala 筆記之 Option Some None 

避免null使用

大多數語言都有一個特殊的關鍵字或者對象來表示一個對象引用的是“無”,在Java,它是null。在Java 裡,null 是一個關鍵字,不是一個對象,是以對它調用任何方法都是非法的。但是這對語言設計者來說是一件令人疑惑的選擇。為什麼要在程式員希望傳回一個對象的時候傳回一個關鍵字呢?

Scala的Option類型

為了讓所有東西都是對象的目标更加一緻,也為了遵循函數式程式設計的習慣,Scala鼓勵你在變量和函數傳回值可能不會引用任何值的時候使用Option類型。在沒有值的時候,使用None,這是Option的一個子類。如果有值可以引用,就使用Some來包含這個值。Some也是Option的子類。

None被聲明為一個對象,而不是一個類,因為我們隻需要它的一個執行個體。這樣,它多少有點像null關鍵字,但它卻是一個實實在在的,有方法的對象。

應用例子

Option類型的值通常作為Scala集合類型(List,Map等)操作的傳回類型。比如Map的get方法:

1

2

3

4

5

6

7

8

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"

res

:

Option[String] 

=

Some(Paris)

scala> capitals get 

"North Pole"

res

1

:

Option[String] 

=

None

Option有兩個子類别,Some和None。當程式回傳Some的時候,代表這個函式成功地給了你一個String,而你可以透過get()函數拿到那個String,如果程式傳回的是None,則代表沒有字元串可以給你。

在傳回None,也就是沒有String給你的時候,如果你還硬要調用get()來取得 String 的話,Scala一樣是會抛出一個NoSuchElementException異常給你的。

我們也可以選用另外一個方法,getOrElse。這個方法在這個Option是Some的執行個體時傳回對應的值,而在是None的執行個體時傳回傳入的參數。換句話說,傳入getOrElse的參數實際上是預設傳回值。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

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

res

3

:

String 

=

Paris

scala> (capitals get 

"North Pole"

) getOrElse 

"Oops"

res

7

:

String 

=

Oops

scala> capitals get 

"France"

getOrElse 

"Oops"

res

8

:

String 

=

Paris

通過模式比對分離可選值,如果比對的值是Some的話,将Some裡的值抽出賦給x變量:

1

2

3

4

def

showCapital(x

:

Option[String]) 

=

match

{

case

Some(s) 

=

> s

case

None 

=

"?"

}

提示

Scala程式使用Option非常頻繁,在Java中使用null來表示空值,代碼中很多地方都要添加null關鍵字檢測,不然很容易出現NullPointException。是以Java程式需要關心那些變量可能是null,而這些變量出現null的可能性很低,但一但出現,很難查出為什麼出現NullPointerException。

Scala的Option類型可以避免這種情況,是以Scala應用推薦使用Option類型來代表一些可選值。使用Option類型,讀者一眼就可以看出這種類型的值可能為None。

實際上,多虧Scala的靜态類型,你并不能錯誤地嘗試在一個可能為null的值上調用方法。雖然在Java中這是個很容易犯的錯誤,它在Scala卻通不過編譯,這是因為Java中沒有檢查變量是否為null的程式設計作為變成Scala中的類型錯誤(不能将Option[String]當做String來使用)。是以,Option的使用極強地鼓勵了更加彈性的程式設計習慣。

詳解Option[T]

在Scala裡Option[T]實際上是一個容器,就像數組或是List一樣,你可以把他看成是一個可能有零到一個元素的List。

當你的Option裡面有東西的時候,這個List的長度是1(也就是 Some),而當你的Option裡沒有東西的時候,它的長度是0(也就是 None)。

for循環

如果我們把Option當成一般的List來用,并且用一個for循環來走訪這個Option的時候,如果Option是None,那這個for循環裡的程式代碼自然不會執行,于是我們就達到了不用檢查Option是否為None這件事。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

scala> 

val

map

1

=

Map(

"key1"

-> 

"value1"

)

map

1

:

scala.collection.immutable.Map[String,String] 

=

Map(key

1

-> value

1

)

scala> 

val

value

1

=

map

1

.get(

"key1"

)

value

1

:

Option[String] 

=

Some(value

1

)

scala> 

val

value

2

=

map

1

.get(

"key2"

)

value

2

:

Option[String] 

=

None

scala> 

def

printContentLength(x

:

Option[String]) {

|   

for

(c <- x){

|     println(c.length)

|   }

| }

printContentLength

:

(x

:

Option[String])Unit

scala> printContentLength(value

1

)

6

scala> printContentLength(value

2

)

map操作

在函數式程式設計中有一個核心的概念之一是轉換,是以大部份支援函數式程式設計語言,都支援一種叫map()的動作,這個動作是可以幫你把某個容器的内容,套上一些動作之後,變成另一個新的容器。

現在我們考慮如何用Option的map方法實作

length: xxx

的輸出形式:

1

2

3

4

5

scala> value

1

.map(

_

.length).map(

"length: "

_

).foreach(println)

length

:

6

scala> value

1

.map(

"length: "

_

.length).foreach(println)

length

:

6

透過這樣「轉換」的方法,我們一樣可以達成想要的效果,而且同樣不用去做「是否為 None」的判斷。