第16章：Scalaの等価性

今回は __等価性__ だ。

等価という考え方は非常に大事だから 
是非マスターしようぜ！

# 等価
等価を考える上で、大事なことが2つあるんだ。 
__等価性__ と __同一性__ だ。

それぞれを説明してみるよ。

## 等価性
__同値性__ と呼ぶこともあるよ。 
__あるオブジェクトA、Bがあったとき、2つが全く同じ値を持つことを等価__ という。

等価性を判定するのは簡単。`==`を使えば良い。 
ちなみに`!=`は、 __否定__ の判定になるぞ。

 では、動かしてみようぜ！

```scala:Equivalence.scala
object Equivalence {

  def main(args: Array[String]): Unit = {
    val domain1 = new Domain1(1, "富樫")
    val domain2 = new Domain1(1, "富樫")

    println(domain1 == domain2)
    println(domain1 != domain2)

    val domain3 = new Domain1(2, "虎丸")

    println("==:", domain1 == domain3)
    println("!=:", domain1 != domain3)
  }

}

class Domain1(val id: Long, val name: String) {
  def canEqual(other: Any) = {
    other.isInstanceOf[Domain1]
  }

  override def equals(other: Any) = {
    other match {
      case that: Domain1 =>
        that.canEqual(Domain1.this) && id == that.id && name == that.name
      case _ => false
    }
  }

  override def hashCode() = {
    val prime = 41
    prime * (prime + id.hashCode) + name.hashCode
  }
}
```

これが動かした結果だ！

```scala
$ scala Equivalence
true   // domain1 == domain2
false  // domain1 != domain2
false  // domain1 == domain3
true   // domain1 != domain3
```

`domain1`と`domain2`は同じフィールド値を持つので、等価ということだ。

### 判定には何を使っているのか
気づいてくれてたかもしれないけど 
勝手にフィールドで判定してくれるわけではないんだ。

判定には`equals`メソッドを使うんだ。 
`==`を使用すると、`equals`に判定を委譲するんだ。 
そのため、`equals`の実装が必要だ。 

なのでクラス`Domain`に以下のメソッドを追加している。

- `equals`
- `hashCode`
- `canEqual`

ここはどう実装したのかって？  
EclipseのScala-IDEプラグインを使って生成している。  
詳細に入るとは混みいってくるので、今回はゴメンな。

## 同一性
次に同一性を見てみよう。

__参照の等価性__の意味。 
__あるオブジェクトA、Bがあったとき、2つが同じオブジェクトであれば同一__ という。

同一性を判定するには、`eq`を使用する。
ちなみに`ne`は、 __否定__ の判定になるぞ。

こっちも、動かしてみようぜ！

```scala:Identity.scala
object Identity {

  def main(args: Array[String]): Unit = {
    val domain1 = new Domain2(1, "富樫")
    val domain2 = domain1

    println(domain1 eq domain2)
    println(domain1 ne domain2)

    val domain3 = new Domain2(1, "富樫")

    println(domain1 eq domain3)
    println(domain1 ne domain3)
  }

}

class Domain2(val id: Long, val name: String) {
  def canEqual(other: Any) = {
    other.isInstanceOf[Domain2]
  }

  override def equals(other: Any) = {
    other match {
      case that: Domain2 =>
        that.canEqual(Domain2.this) && id == that.id && name == that.name
      case _ => false
    }
  }

  override def hashCode() = {
    val prime = 41
    prime * (prime + id.hashCode) + name.hashCode
  }
}
```

これが結果だ！

```scala
$ scala Identity
true   // domain1 eq domain2
false  // domain1 ne domain2
false  // domain1 eq domain3
true   // domain1 ne domain3
```

`domain1`と`domain3`は単なる等価だから
`eq`の結果は`true`になっていないね！

# Javaとの違い
Javaとは記述が違うから、一応書いておくね。

```java:Equals.java
public class Equals {
	public static void main(String[] args) {
		final String a = new String("test");
		final String b = new String("test");

		System.out.println(a == b);
		System.out.println(a.equals(b));
	}
}
```

実行した結果がこれだ。
Javaの動かし方は、記述しないからね。

```java
false  // a == b
true   // a.equals(b)

```

Javaでは以下で表すんだ。

- 等価性
    - `equals`
- 同一性
    - `==`

# ケースクラス
最後に、ケースクラスを簡単に紹介だ！

ケースクラスを使うと、 `equals`とかの実装が自動で定義されるようだ。
何が定義されるかは、まだ把握できていないんだ。。。

一先ず`equals`が実装されるか見てみよう！

```scala:CaseClass.scala
object CaseClass {

  def main(args: Array[String]): Unit = {
    val domain1 = new Domain3(1, "富樫")
    val domain2 = new Domain3(1, "富樫")

    println("==:", domain1 == domain2)
    println("!=:", domain1 != domain2)

    val domain3 = new Domain3(2, "虎丸")

    println("==:", domain1 == domain3)
    println("!=:", domain1 != domain3)

  }

  case class Domain3(val id: Long, val name: String)
}
```

実行してみよう！

```scala
scala CaseClass
true   // domain1 == domain2
false  // domain1 != domain2
false  // domain1 == domain3
true   // domain1 != domain3
```

Equivalence.scalaの結果と同じになったね！

明示的な実装をせず、ケースクラスにもしない場合、
`equals`がどうなるるか気になったので次のソースを書いてみよ。

```scala:NoneCaseClass.scala
object NoneCaseClass {

  def main(args: Array[String]): Unit = {
    val domain1 = new Domain4(1, "富樫")
    val domain2 = domain1

    println("==:", domain1 == domain2)
    println("!=:", domain1 != domain2)

    val domain3 = new Domain4(2, "虎丸")

    println("==:", domain1 == domain3)
    println("!=:", domain1 != domain3)

  }

  class Domain4(val id: Long, val name: String)
}
```

動かすと次のようになる。

```scala
$ scala NoneCaseClass
true   // domain1 == domain2
false  // domain1 != domain2
false  // domain1 == domain3
true   // domain1 != domain3
```

これは、同一性の実装がしてあると推測できるね。
実際はどうなっているかは、Jadを使って見てみてね！

ケースクラスについてもう一つ。

そもそも`equals`の実装は、少なくとも以下の2パターンはあると思う。

- 全フィールドで見ないといけない
- 識別子となるフィールドのみを見る

なので、なんでもかんでもケースクラス使えばOK！
は __NG__  だ！

# まとめ
今回は、等価性だったけどどうだった？ 
実際にアプリケーション作ると等価性に救われることが 
良くあるぞ！

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