==演算子とequalsメソッドの違い【Scala編】

[先日の投稿](http://qiita.com/hys-rabbit/items/1251e86d9d22507d309a)で、Javaでの同一同値比較について書きました。そして現在の業務ではScalaを触ることもありまして、ついでにScalaについても少し調べてみました。

# プリミティブ型と参照型
Scalaについてですが、調べたところプリミティブ型は存在しないとのことです。前回の投稿で[shiracamusさん](http://qiita.com/shiracamus)にいただいたコメントの中で知りましたがPythonも同様のようで、全てがオブジェクトとして扱われる言語のようです。そしてScalaでは全てのクラス、オブジェクトはAnyクラスを継承しています。その直下にはAnyValクラスとAnyRefクラスがあり、Javaでのプリミティブ型に近い役割を担っているクラスはAnyValクラスを、それ以外のクラスはAnyRefクラスを継承しています。

    Any
     ├── AnyVal <- Javaでいうプリミティブ型に相当する
     │    ├── Boolean
     │    ├── Byte
     │    ├── Char
     │    ├── Double
     │    ├── Float
     │    ├── Int
     │    ├── Long
     │    └── Short
     └── AnyRef <- Javaでいう参照型に相当する
          │
          .
          .
          .
ちなみにAnyValクラスを継承しているクラスにはnullを代入できないようです。

# 同値比較
ではScalaでの"==演算子"はどのような働きをするのでしょうか？実際に動かしてみました。

```scala
object Main {
  
  def main(args: Array[String]) = {
    
    val stringA = new String("文字列")
    val stringB = new String("文字列")
    
    println(stringA == stringB) // true
    println(stringA.equals(stringB)) // true
    
  }
  
}
```

"==演算子"も"equalsメソッド"も共にtrueを返しています。ここで一つ致命的な間違いを知るわけなんですが、そもそも<font color=red><b>Scalaでは==は演算子ではなくメソッド</b></font>だということです。全てがオブジェクトなので、メソッドだということにも納得がいきます。ちなみに"=="は ".==()"とも書くことができ、Anyクラスが持つメソッドです。そしてequalsメソッドを実装していればそちらに処理を委譲するそうです。つまり

- stringA == stringB
- stringA.==(stringB)
- stringA.equals(stringB)

は全て同じ動作となります。ScalaのStringはJavaのStringを内部で持っていて、equalsメソッドはjava.lang.String#equalsを使用しています。

ではequalsメソッドを持たないクラスではどうなるのか、equalsメソッドを持たないクラスを作り、==メソッドを使ってみたいと思います。

```scala
class Value(val value: String)

object Main {
  
  def main(args: Array[String]) = {
    
    val valeuA = new Value("文字列")
    val valeuB = new Value("文字列")
    
    println(valeuA == valeuB) // false
    
  }
  
}
```
equalsメソッドを実装していないと、java.lang.Object#equalsが呼ばれるようです。Object#equalsでは同一比較を行うので、falseになると。

ではequalsメソッドを実装して再度実行してみる

```scala
class Value(val value: String) {
  def canEqual(a: Any) = a.isInstanceOf[Value]
  override def equals(that: Any): Boolean =
      that match {
          case that: Value => that.canEqual(this) && this.hashCode == that.hashCode
          case _ => false
   }
  override def hashCode: Int = {
      val prime = 31
      var result = 1
      result = prime * result + (if (value == null) 0 else value.hashCode)
      return result
  }
}

object Main {
  
  def main(args: Array[String]) = {
    
    val valeuA = new Value("文字列")
    val valeuB = new Value("文字列")
    
    println(valeuA == valeuB) // true
    
  }
  
}
```
実装したequalsメソッドを呼び出しtrueとなりました。
ちなみにcase classにすると自動でequalsメソッドが実装されます。

```scala
case class Value(value: String)

object Main {
  
  def main(args: Array[String]) = {
    
    val valeuA = new Value("文字列")
    val valeuB = new Value("文字列")
    
    println(valeuA == valeuB) // true
    
  }
  
}
```

# 同一比較
オブジェクトに"=="を使うとJavaと違い、同値比較をすることがわかりました。では同一比較はどうするのかというと"eqメソッド"と"neメソッド"を使います。eqはequalの略（たぶん）で同一判定、neはnot equalの略で非同一判定を行います。

```scala
object Main {
  
  def main(args: Array[String]) = {
    
    val stringA = new String("文字列")
    val stringB = new String("文字列")
    
    println(stringA eq stringB) // false
    println(stringA ne stringB) // true
    
  }
  
}
```
"eq"と"ne"もそれぞれ".eq()"、".ne()"と表現することができます。これらはAnyRefクラスのメソッドで、参照型系のクラスで使用可能です。独自で定義したクラスも暗黙的にAnyRefクラスを継承するので使用可能です。


# おまけ
同値比較をする"=="が、演算子ではなくメソッドだということがわかりましたが、nullに使用するとどうなるのか。Javaでは演算子なのでnull比較をすることができましが、Scalaではメソッドなので、NullPointerExceptionになってしまうんじゃないかという懸念があります。

```scala
object Main {
  
  def main(args: Array[String]) = {
    
    val blank = null
    val string = "文字列"
    
    println(blank == string) // false
    
  }
  
}

```
無事実行できました。[こちらの記事](http://qiita.com/crispy/items/bb44787f3ee64443df93)によると、コンパイルするとnullチェックを差し込むようになるそうです。ますますScalaが好きになりますね。


# 最後に
Scalaでは同値比較に"=="や".==()"、".equals()"と様々な記法がありますが、"=="に統一して書くのが良さそうです。単純に文字数が二文字でタイピングが楽だし、可読性も高いかと。JavaのようにNullPointerExceptionを気にする必要もないので。
ScalaはJavaと似ていて、Javaエンジニアなら何となくでコードが書けてしまうと思っていましたが、それは間違いでした。Javaのような感覚でコーディングしていると痛い目を見そうなので、これからもScala学習に精進したいと思います。