case class‥ってなんだ?

# case class
## コップ本曰く
- 通常のクラスと似ているいくつか重要な違いをもつ、不変データをモデリングするのに適したクラス
- 紋切り型コードをあまり使わずオブジェクトのパターンマッチを行えるようにするScalaの記法(ボイラープレートが減る。パターンマッチのためにunapplyをがんばって記述しなくていいってことかな‥？)

## 宣言すると…
- 便利なメソッドが定義される
- コンパニオンオブジェクトが定義される
- 基本コンストラクタ引数全てが「val」で宣言されたフィールドになる
    - 明示的に「var」のフィールドにできる
    - 基本は「val」になるっているから`不変データをモデリングするのに適したクラス`として位置づけているんだろうなと考えられそう

```
scala> case class Person(name: String, age: Int)
// name: String
// age: Int
// がプロパティになる
```

## 便利なメソッド
- copy
    - 一部(全部でも)のフィールドを変更した新しいインスタンスを生成する

```
scala> val oldTaro = taro.copy(age = 40)
scala> println(oldTaro.age)
40
scala> println(taro.age)
20
```

- toString
    - いい感じに文字列で表現してくれる
   
```
scala> val taro = Person("taro", 20)
scala> println(taro.toString())
Person(taro,20)
```

- equals
    - `==`は`equals`が定義されていると処理が`equals`に移譲される

```
// 同一のインスタンスをさしていたら"true"が返る
scala> val taro2 = taro
taro2: Person = Person(taro,20)
scala> taro2 == taro
res6: Boolean = true
// 別のインスタンス、別の値を持っていたら"false"が返る
scala> val ziro = Person("ziro", 20)
ziro: Person = Person(ziro,20)
scala> ziro == taro
res7: Boolean = false
// 別インスタンス、同一で値だったら"true"が返る
scala> val cloneTaro = Person("taro", 20)
cloneTaro: Person = Person(taro,20)
```

## コンパニオンオブジェクト
- apply
    - new がなくてもインスタンス生成できる
- unapply
    - コンストラクタパターンを使ったパターンマッチができる

## toString
- いい感じに文字列で表現してくれる
- いい感じってどういうことだ？
- scalaコンソールを`scala -Xprint:typer`で呼び出して、ASTをのぞいて見る
- `def toString(): String = scala.runtime.ScalaRunTime._toString(Person.this);`
  - `scala.runtime.ScalaRunTime`に答えはあるようだ
- toStringを追うために必要な箇所だけ抜粋

```
case class Person extends AnyRef with Product with Serializable
          override <synthetic> def productIterator: Iterator[Any] = scala.runtime.ScalaRunTime.typedProductIterator[Any](Person.this);

          override <synthetic> def productPrefix: String = "Person";
          <synthetic> def productArity: Int = 2;
          <synthetic> def productElement(x$1: Int): Any = x$1 match {
            case 0 => Person.this.name
            case 1 => Person.this.age
            case _ => throw new IndexOutOfBoundsException(x$1.toString())
          };
          override <synthetic> def toString(): String = scala.runtime.ScalaRunTime._toString(Person.this);
```



### scala.runtime.ScalaRunTime
```scala.runtime.ScalaRunTime
def _toString(x: Product): String = x.productIterator.mkString(x.productPrefix + "(", ",", ")")
```


- `scala.runtime.ScalaRunTime`では`Product`を実装したオブジェクトを取り、`productIterator`の要素を`String`にして結合しているようだ
    - `Product`を実装する要素に`productIterator`と`productPrefix`があるようだがcase classでは二つとも`override`している
    - `productPrefix`はcase classならクラス名になっていたね
    - 渡されたオブジェクトがcase classなら`productIterator`が一つ一つのフィールドを文字列にしているようだ
    - case classの`productIterator`ではcase class Personの定義を見ると、`typedProductIterator`に処理を委譲している
        - 委譲先では`productElement`を`productArity`の数だけ呼び出すことですべてのフィールドを返している 

```scala.runtime.ScalaRunTime
/** A helper for case classes. */
def typedProductIterator[T](x: Product): Iterator[T] = {
  new AbstractIterator[T] {
    private var c: Int = 0
    private val cmax = x.productArity
    def hasNext = c < cmax
    def next() = {
      val result = x.productElement(c)
      c += 1
      result.asInstanceOf[T]
    }
  }
}
```

- case class のtoStringはProductの実装から来ているんだね
  - とくにcase classでは`productPrefix`では名前を、`productElement`でフィールドを全て返すように実装しているから`Person("taro", 22)`となるんだな。納得

## どうだっていいかもしれないこと
- case class の `case`ってネーミングどこから来ているんですかね？