第14章：ScalaのOption型とnullを語る

Option型って知っているかい？これマジ凄いよ！

NullPointerException、通称 __ヌルポ__ 。
少し込み入ったJavaアプリを作って動かすと、大抵発生するよね。

nullチェックを入れてたり、規約で縛ったりして対応することになると思います。
だけど、コンパイル時に見つけることができれば、、、と思ったことない？

Option型を上手く使えれば、コンパイル時にnullチェックみたいなことができてしまうんだ。
とても素敵だよね！

では、Option型について語ってみます。

# Optionとは
値があるかないかを表す型です。箱って言っても良いかもですね。
Optionは更に2種類のサブクラスを持っている。

- Some
- None

## Some
値があることを表す型。値を持っている。

## None
値がないことを表す型。

# Optionを使ってみる
淡白に説明してみたので、早速使ってみよう！

## Optionを取得する
Mapを作って、getしてみるよ！
getメソッドはOptionを返すんだ。

```scala
scala> val map = Map(1 -> "Moses", 2 -> "Lucas", 3 -> "Henderson", 5 -> null)
map: scala.collection.immutable.Map[Int,String] = Map(1 -> Moses, 2 -> Lucas, 3 -> Henderson, 5 -> null)

scala> map get 1
res0: Option[String] = Some(Moses)

scala> map get 3
res1: Option[String] = Some(Henderson)

scala> map get 4
res2: Option[String] = None
```

getメソッドに`1`、`3`を渡すと`Some`が返って、値を持っていることがわかるね。
一方`4`を渡すと、該当する値は存在しないから`None`が返ってるね。

### 値がnull
では、getメソッドに`5`を渡すとどうなるかな？
試してみよう！

```scala
scala> map get 5
res3: Option[String] = Some(null)
```

どう？
予想した通りになったかな？

`Some`が返ってきているから、値が存在するということだね。
ただ、その値自体が`null`ってことなんだ。

つまり、

- Mapにキーが存在している場合は`Some`を返し、
- Mapにキーが存在しない場合は`None`を返す

ってことだね。

そして、キーに該当する値が`null`の場合も、もちろんあるってこと。

`null`を扱うのが嫌だという場合は、例えば以下を意識してみよう。

- 可能な限り変数をイミュータブルにする
- テーブルのカラムはNot null制約使う

とは言ってもJavaとは切れない関係だから、Javaのライブラリを使ってしまうと
どうしても`null`が混入してしまうね。

## Optionから値を取り出す
値があることがわかれば、実際の値を欲しくなるよね？
というか取り出さないと意味ないよね。

取り出すのは簡単だよ。Optionのgetメソッドを使うだけだ。

さっきはMapのgetメソッドを使ったんだけど、ここら辺は
ごっちゃにならないように気をつけてね！

```scala
scala> val person = map get 2
person: Option[String] = Some(Lucas)

scala> person.get
res3: String = Lucas
```

Noneの場合もやってみよう。

```scala
scala> val nonePerson = map get 4
nonePerson: Option[String] = None

scala> nonePerson.get
java.util.NoSuchElementException: None.get
	at scala.None$.get(Option.scala:313)
	at scala.None$.get(Option.scala:311)
	at .<init>(<console>:10)
	at .<clinit>(<console>)
	at .<init>(<console>:7)
	at .<clinit>(<console>)
	at $print(<console>)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:734)
	at scala.tools.nsc.interpreter.IMain$Request.loadAndRun(IMain.scala:983)
	at scala.tools.nsc.interpreter.IMain.loadAndRunReq$1(IMain.scala:573)
	at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:604)
	at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:568)
	at scala.tools.nsc.interpreter.ILoop.reallyInterpret$1(ILoop.scala:756)
	at scala.tools.nsc.interpreter.ILoop.interpretStartingWith(ILoop.scala:801)
	at scala.tools.nsc.interpreter.ILoop.command(ILoop.scala:713)
	at scala.tools.nsc.interpreter.ILoop.processLine$1(ILoop.scala:577)
	at scala.tools.nsc.interpreter.ILoop.innerLoop$1(ILoop.scala:584)
	at scala.tools.nsc.interpreter.ILoop.loop(ILoop.scala:587)
	at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply$mcZ$sp(ILoop.scala:878)
	at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply(ILoop.scala:833)
	at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply(ILoop.scala:833)
	at scala.tools.nsc.util.ScalaClassLoader$.savingContextLoader(ScalaClassLoader.scala:135)
	at scala.tools.nsc.interpreter.ILoop.process(ILoop.scala:833)
	at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:83)
	at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:96)
	at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:105)
	at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)

```

かなり悲惨なことになってしまった。。。
こうなると、SomeかNoneの判定は必須だね。

# パターンマッチ
Noneのgetメソッドを使うとExceptionが発生することがわかったので、
判定が必須になりました。
判定はどうする？`if`キーワード使っちゃう？

まだ語っていないんだけど、 __パターンマッチ__  が便利だ。
パターンマッチを使うと読みやすいし、強力な機能も使えるんだけど
詳しい説明は別の章で。

今回は紹介がてらソースコードを載せるね。

```scala:Option1.scala
object Option1 {

  def main(args: Array[String]): Unit = {
    val map = Map(1 -> "Moses", 2 -> "Lucas", 3 -> "Henderson")

    def check(o: Option[String]) {
      o match {
        case Some(s) => println(s)
        case None => println("Not exist.")
      }
    }

    val some = map get (2)
    val none = map get (4)

    check(some)
    check(none)
  }

}
```

実行してみよう。

```
$ scalac Option1.scala
$ scala Option1
Lucas
Not exist.
```

となりました。

# コンパイル時のチェック
冒頭でちょろっと述べたけど、 コンパイル時のnullチェックみたいなものを説明します。

ここまで読んでくれていれば、勘の鋭い人なら気づいているかもしれない。
そうです、nullチェックとは言いつつNoneチェックであり、
パターンマッチで実現できるのです。

もちろんSomeチェックでもあるんだけど、
大抵の場合忘れるのは、何かが存在しない場合の方なので。

早速見てみましょう。

```scala:Option2.scala
object Option2 {

  def main(args: Array[String]): Unit = {
    val map = Map(1 -> "Moses", 2 -> "Lucas", 3 -> "Henderson")

      def check(o: Option[String]) {
        o match {
          case Some(s) => println(s)
        }
      }

    val some = map get (2)

    check(some)
  }

}
```

コンパイルしてみると

```
$ scalac Option2.scala
Option2.scala:7: warning: match may not be exhaustive.
It would fail on the following input: None
        o match {
        ^
one warning found
```

こんなの出ちゃいました。

これでNone忘れを回避できるようになるね！！

ちなみにEclipseで試してみたところ、
ソースコードの左に黄色のwarningを出してくれていました。

# Nullオブジェクトパターン
ここは蛇足ではあるんだけど。

Nullオブジェクトパターンって知っているかな？
Option型を知った時、このパターンが頭に浮かんできました。

## Nullオブジェクトパターンとは
あるインタフェースに対して、空実装をした具象クラス。
つまりどのメソッドも何も処理をしない。

正常系のロジックを通すことが可能となる。

なので以下が可能となる。

- ヌルポ撲滅
- nullチェックの分岐撲滅

OptionもNullオブジェクトパターンに近い考え方なのではないかと
感じております。

# まとめ
Option型はどうだった？

ヌルポをコンパイル時にチェックできるようになる仕組みって
凄くないですか？

あと
__Optionのgetは使わないで、パターンマッチを使おう！__

今回も
__体で感じてくれたかな？__