結果をラップしたSome(値)かNoneを持つクラス。
nullかもしれないものはOptionでラップしておくことで、すべてのクラスに対してnullを気にしなければいけない現状を解決してくれるかもしれない。
Scaladoc
http://www.scala-lang.org/api/current/scala/Option.html
引数が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)
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などを利用。
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はそうした機能を一手に引き受けてくれる。
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)
Noneならnullを返す。Someの場合はgetと同じ挙動。
// Someの場合
Option("foo").orNull
//=> java.lang.String = foo
// Noneの場合
Option(null).orNull
//=> Null = null
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は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
条件付きのisDefined。たとえば中身がnullでないことを条件として付けたい場合などに使える。
// 中身が != nullの場合のみtrueが返るよう指定
Some("foo").exists(_ != null)
//=> Boolean = true
// nullの場合はfalseになる
Some(null).exists(_ != null)
//=> Boolean = false
// 整数の場合のみなどの指定もできる
Some(-1).exists(_ >= 0)
//=> Boolean = false
Noneを返す。
Option.empty
//=> Option[Nothing] = None
// 型指定したNoneも取れる
Option.empty[String]
//=> Option[String] = None
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も用意されている。Noneなら何もせず、Someなら中身に対して処理するような動きになる。
Some(1).foreach(println)
//=> 1
None.foreach(println)
//=> 何も処理されない
Noneじゃなければ指定した関数を実行するようなケースでは、便利かもしれない。
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()
これも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は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
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に結果を載せている。なるほど、これは使うかもしれない。