概要

結果をラップしたSome(値)かNoneを持つクラス。

nullかもしれないものはOptionでラップしておくことで、すべてのクラスに対してnullを気にしなければいけない現状を解決してくれるかもしれない。

Scaladoc
http://www.scala-lang.org/api/current/scala/Option.html

@CretedDate 2011/05/03
@Versions Scala2.8

コンストラクタ

引数がnullならNoneを、それ以外ならSome(引数)を返す。NoneとSomeはそれぞれOptionを継承している。ちなみにOption自体はabstract。

NoneはisEmptyに対してtrueを、getに対してNoSuchElementExceptionを返す。

SomeはisEmptyに対してfalseを、getに対して中身の値を返す。

Option("foo")
  //=> Option[java.lang.String] = Some(foo)

Option(null)
  //=> Option[Null] = None

尚、Someのコンストラクタを呼び出せば、nullが入ったSomeも作れる。

scala> val some = Some(null)
some: Some[Null] = Some(null)

get

Optionでラップされた中身を取得する。以下はSomeから取り出す場合。

val opt = Option(1)
  //=> Option[Int] = Some(1)

opt.get
  //=> Int = 1

次にNoneから取り出す場合。Noneに対してgetしようとすると例外が発生する。

val opt = Option(null)
  //=> Option[Null] = None

opt.get
  //=> java.util.NoSuchElementException: None.get

「絶対Someだ。Someじゃなければ例外で結構」というシチュエーションではgetを利用。Noneかもと思う場合は、次のgetOrElseなどを利用。

getOrElse

Noneだったらデフォルト値を、Someだったらgetの結果を返す(実際にはisEmptyで判別)。

// Someの場合は、ラップされてるものがそのまま返る
scala> val opt = Option("foo").getOrElse("bar")
  //=> opt: java.lang.String = foo

// Noneの場合は、引数で指定したデフォルト値が返る
scala> val opt = Option(null).getOrElse("bar")
  //=> opt: java.lang.String = bar

nullだったらデフォルト値を、というような機能はJavaでもいろいろ見かけるけど、Optionはそうした機能を一手に引き受けてくれる。

CollectionからNoneでない値のみ取り出す

SomeとNoneが含まれるCollectionから、Noneでない値を取り出したい場合は、forループで可能。

// [1, None, 2]が入ってるOptionのListがあったとする
scala> val list = List(Some(1), None, Some(2))
list: List[Option[Int]] = List(Some(1), None, Some(2))

// 普通にforで回すと、すべて出力される
scala> for(x <- list ) println(x)
Some(1)
None
Some(2)

// こう書けば、Someの中身だけがループ可能
scala> for(Some(x) <- list ) println(x)
1
2

flatmapを使うと、Noneを省いた中身のListが出来上がる。

scala> val list = List(Some(1), None, Some(2))

scala> list.flatMap(x => x)
  //=> List[Int] = List(1, 2)

orNull

Noneならnullを返す。Someの場合はgetと同じ挙動。

// Someの場合
Option("foo").orNull
  //=> java.lang.String = foo

// Noneの場合
Option(null).orNull
  //=> Null = null

orElse

Noneだったら、指定したデフォルトのOption型を返す。SomeだったらそのままそのSomeを返す。あまり使うシーンが思いつかない。

Some("foo").orElse(Option("bar"))
  //=> Option[java.lang.String] = Some(foo)

None.orElse(Option("bar"))
  //=> Option[java.lang.String] = Some(bar)

isEmpty / isDefined

isEmptyはSomeならfalse、Noneならtrueを返す。isDefinedはその逆。

// Someの場合(isEmpty)
Option("foo").isEmpty
  //=> Boolean = false

// Noneの場合(isEmpty)
Option(null).isEmpty
  //=> Boolean = true

// Someの場合(isDefined)
Option("foo").isDefined
  //=> Boolean = true

// Noneの場合(isDefined)
Option(null).isDefined
  //=> Boolean = false

exists

条件付きのisDefined。たとえば中身がnullでないことを条件として付けたい場合などに使える。

// 中身が != nullの場合のみtrueが返るよう指定
Some("foo").exists(_ != null)
  //=> Boolean = true

// nullの場合はfalseになる
Some(null).exists(_ != null)
  //=> Boolean = false

// 整数の場合のみなどの指定もできる
Some(-1).exists(_ >= 0)
  //=> Boolean = false

empty

Noneを返す。

Option.empty
  //=> Option[Nothing] = None

// 型指定したNoneも取れる
Option.empty[String]
  //=> Option[String] = None

map / flatMap

Listでよく利用されているmapとflatMapも用意されている。使い方もListのものと同じ感じ。

Option("foo").map { x => x.size }
  //=> Option[Int] = Some(3)

Option(List(1, 2, 3)).flatMap { x => Option(x.sum) }
  //=> Option[Int] = Some(6)

mapとflatMapの結果はOption型である必要があるらしい。

foreach

foreachも用意されている。Noneなら何もせず、Someなら中身に対して処理するような動きになる。

Some(1).foreach(println)
  //=> 1

None.foreach(println)
  //=> 何も処理されない

Noneじゃなければ指定した関数を実行するようなケースでは、便利かもしれない。

iterator / toList

iteratorやtoListなどのCollection的なものも用意されている。

// iteratorは名前の通りIteratorを返す
Some("foo").iterator.next
  //=> java.lang.String = foo

// Noneの場合は空のIteratorが返る
None.iterator
  //=> Iterator[Nothing] = empty iterator

// toListはList型を返す
Some("foo").toList
  //=> List[java.lang.String] = List(foo)

// Noneの場合は空のListを返す
None.toList
  //=> List[Nothing] = List()

// 型を持ったNoneなら、ちゃんと型は引き継がれる
Option(null: String).toList
  //=> List[String] = List()

collect

これもListなどで利用されている関数。さらっとcaseが書けるのは便利かもしれない。

Option(1).collect{ case 1 => "ONE"; case _ => "OTHER" }
  //=> Option[java.lang.String] = Some(ONE)

Option(2).collect{ case 1 => "ONE"; case _ => "OTHER" }
  //=> Option[java.lang.String] = Some(OTHER)

Noneに対して実行すると、Noneが返って来ます。型は変わります。

val none = Option.empty[Int]

none.collect{ case 1 => "ONE"; case _ => "OTHER" }
  //=> Option[java.lang.String] = None

withFilter

withFilterは戻り値にWithFilterを返す。WithFilterはOptionにフィルタをかけた上で、map、flatMap、foreachなどを実行できる。

// 0以上の数値を条件とするフィルタを設定
val filter = Option(1) withFilter(_ > 0)

filter.foreach(println)
  //=> 1

filter.map(x => x * 2)
  //=> Some(2)
// Someの中身がフィルタの条件に当てはまらない場合は、スキップされる
val filter = Option(-1) withFilter(_ > 0)
filter.foreach(println)
  //=> 何も実行されない

// そもそもNoneの場合も同様
val filter = Option.empty[Int] withFilter(_ > 0)
filter.foreach(println)
  //=> 何も実行されない

WithFilterクラスもwithFilter関数を持っているので、連結可能。

Option(1) withFilter(_ > 0) withFilter(_ % 2 == 1) foreach(println)
  //=> 1

toLeft / toRight

Left、もしくはRightクラスのインスタンスを返す。LeftとRightはscala.Eitherクラスで定められているみたいだけど、使ったことがないので良くわからない。

Leftはfailure、RightはSomeみたいなものを格納するらしい。

// SomeをtoLeftすると、LeftにSomeの値が入る
Option("foo").toLeft("bar")
  //=> Product with Either[java.lang.String,java.lang.String] = Left(foo)

// NoneをtoLeftすると、Rightに引数の値が入る
None.toLeft("bar")
  //=> Product with Either[Nothing,java.lang.String] = Right(bar)

// Rightだと逆
Option("foo").toRight("bar")
  //=> Product with Either[java.lang.String,java.lang.String] = Right(foo)

LeftとRightには違う型が指定できるので、成功ならRightに結果を、失敗ならLeftにエラーを格納するみたいなこともできるっぽい。

Option("foo").toRight(new RuntimeException("bar"))
  //=> Product with Either[java.lang.RuntimeException,java.lang.String] = Right(foo)

Eitherはscala.concurrent.opsなどで利用されており、そこではこんな関数が定義されている。

private def tryCatch[A](body: => A): Either[Throwable, A]

ここではtryCatchを共通化する高階関数で、LeftにThrowable、Rightに結果を載せている。なるほど、これは使うかもしれない。