## 30 Object Equality

Sample run of chapter's interpreter examples

### 30.1 Equality in Scala

```
final def == (that: Any): Boolean =
if (null eq this) {null eq that} else {this equals that}

```

### 30.2 Writing an equality method

```
var hashSet: Set[C] = new collection.immutable.HashSet
hashSet += elem1
hashSet contains elem2    // returns false!

class Point(val x: Int, val y: Int) { ... }

// In file equality/Points1.scala

// An utterly wrong definition of equals
def equals(other: Point): Boolean =
this.x == other.x && this.y == other.y

scala> val p1, p2 = new Point(1, 2)
p1: Point = Point@79f0ec
p2: Point = Point@1b8424e

scala> val q = new Point(2, 3)
q: Point = Point@d990db

scala> p1 equals p2
res0: Boolean = true

scala> p1 equals q
res1: Boolean = false

scala> import scala.collection.mutable._
import scala.collection.mutable._

scala> val coll = HashSet(p1)
coll: scala.collection.mutable.HashSet[Point] =
Set(Point@79f0ec)

scala> coll contains p2
res2: Boolean = false

scala> val p2a: Any = p2
p2a: Any = Point@1b8424e

scala> p1 equals p2a
res3: Boolean = false

def equals(other: Any): Boolean

// In file equality/Points2.scala

// A better definition, but still not perfect
override def equals(other: Any) = other match {
case that: Point => this.x == that.x && this.y == that.y
case _ => false
}

def ==(other: Point): Boolean = // Don't do this!

scala> val p1, p2 = new Point(1, 2)
p1: Point = Point@67e5a7
p2: Point = Point@1165e21

scala> HashSet(p1) contains p2
res4: Boolean = false

// In file equality/Points3.scala

class Point(val x: Int, val y: Int) {
override def hashCode = 41 * (41 + x) + y
override def equals(other: Any) = other match {
case that: Point => this.x == that.x && this.y == that.y
case _ => false
}
}

// In file equality/Points6.scala

class Point(var x: Int, var y: Int) { // Problematic
override def hashCode = 41 * (41 + x) + y
override def equals(other: Any) = other match {
case that: Point => this.x == that.x && this.y == that.y
case _ => false
}
}

scala> val p = new Point(1, 2)
p: Point = Point@6bc

scala> val coll = HashSet(p)
coll: scala.collection.mutable.HashSet[Point] =
Set(Point@6bc)

scala> coll contains p
res5: Boolean = true

scala> p.x += 1

scala> coll contains p
res7: Boolean = false

scala> coll.iterator contains p
res8: Boolean = true

// In file equality/Points8.scala

object Color extends Enumeration {
val Red, Orange, Yellow, Green, Blue, Indigo, Violet = Value
}

// In file equality/Points5.scala

class ColoredPoint(x: Int, y: Int, val color: Color.Value)
extends Point(x, y) { // Problem: equals not symmetric

override def equals(other: Any) = other match {
case that: ColoredPoint =>
this.color == that.color && super.equals(that)
case _ => false
}
}

scala> val p = new Point(1, 2)
p: Point = Point@6bc

scala> val cp = new ColoredPoint(1, 2, Color.Red)
cp: ColoredPoint = ColoredPoint@6bc

scala> p equals cp
res9: Boolean = true

scala> cp equals p
res10: Boolean = false

scala> HashSet[Point](p) contains cp
res11: Boolean = true

scala> HashSet[Point](cp) contains p
res12: Boolean = false

// In file equality/Points6.scala

class ColoredPoint(x: Int, y: Int, val color: Color.Value)
extends Point(x, y) { // Problem: equals not transitive

override def equals(other: Any) = other match {
case that: ColoredPoint =>
(this.color == that.color) && super.equals(that)
case that: Point =>
that equals this
case _ =>
false
}
}

scala> val redp = new ColoredPoint(1, 2, Color.Red)
redp: ColoredPoint = ColoredPoint@6bc

scala> val bluep = new ColoredPoint(1, 2, Color.Blue)
bluep: ColoredPoint = ColoredPoint@6bc

scala> redp == p
res13: Boolean = true

scala> p == bluep
res14: Boolean = true

scala> redp == bluep
res15: Boolean = false

// In file equality/Points7.scala

// A technically valid, but unsatisfying, equals method
class Point(val x: Int, val y: Int) {
override def hashCode = 41 * (41 + x) + y
override def equals(other: Any) = other match {
case that: Point =>
this.x == that.x && this.y == that.y &&
this.getClass == that.getClass
case _ => false
}
}

// In file equality/Points7.scala

class ColoredPoint(x: Int, y: Int, val color: Color.Value)
extends Point(x, y) {

override def equals(other: Any) = other match {
case that: ColoredPoint =>
(this.color == that.color) && super.equals(that)
case _ => false
}
}

scala> val pAnon = new Point(1, 1) { override val y = 2 }
pAnon: Point = \$anon\$1@6bc

def canEqual(other: Any): Boolean

// In file equality/Points8.scala

class Point(val x: Int, val y: Int) {
override def hashCode = 41 * (41 + x) + y
override def equals(other: Any) = other match {
case that: Point =>
(that canEqual this) &&
(this.x == that.x) && (this.y == that.y)
case _ =>
false
}
def canEqual(other: Any) = other.isInstanceOf[Point]
}

// In file equality/Points8.scala

class ColoredPoint(x: Int, y: Int, val color: Color.Value)
extends Point(x, y) {

override def hashCode = 41 * super.hashCode + color.hashCode
override def equals(other: Any) = other match {
case that: ColoredPoint =>
(that canEqual this) &&
super.equals(that) && this.color == that.color
case _ =>
false
}
override def canEqual(other: Any) =
other.isInstanceOf[ColoredPoint]
}

scala> val p = new Point(1, 2)
p: Point = Point@6bc

scala> val cp = new ColoredPoint(1, 2, Color.Indigo)
cp: ColoredPoint = ColoredPoint@11421

scala> val pAnon = new Point(1, 1) { override val y = 2 }
pAnon: Point = \$anon\$1@6bc

scala> val coll = List(p)
coll: List[Point] = List(Point@6bc)

scala> coll contains p
res16: Boolean = true

scala> coll contains cp
res17: Boolean = false

scala> coll contains pAnon
res18: Boolean = true

```

### 30.3 Defining equality for parameterized types

```
// In file equality/Tree4.scala

trait Tree[+T] {
def elem: T
def left: Tree[T]
def right: Tree[T]
}

object EmptyTree extends Tree[Nothing] {
def elem =
throw new NoSuchElementException("EmptyTree.elem")
def left =
throw new NoSuchElementException("EmptyTree.left")
def right =
throw new NoSuchElementException("EmptyTree.right")
}

class Branch[+T](
val elem: T,
val left: Tree[T],
val right: Tree[T]
) extends Tree[T]

// In file equality/Tree3.scala

class Branch[T](
val elem: T,
val left: Tree[T],
val right: Tree[T]
) extends Tree[T] {

override def equals(other: Any) = other match {
case that: Branch[T] => this.elem == that.elem &&
this.left == that.left &&
this.right == that.right
case _ => false
}
}

\$ fsc -unchecked Tree.scala
Tree.scala:14: warning: non variable type-argument T in type
pattern is unchecked since it is eliminated by erasure
case that: Branch[T] => this.elem == that.elem &&
^

scala> val b1 = new Branch[List[String]](Nil,
|      EmptyTree, EmptyTree)
b1: Branch[List[String]] = Branch@158c7fa

scala> val b2 = new Branch[List[Int]](Nil,
|      EmptyTree, EmptyTree)
b2: Branch[List[Int]] = Branch@1f4a968

scala> b1 == b2
res19: Boolean = true

// In file equality/Tree3.scala

case that: Branch[t] => this.elem == that.elem &&
this.left == that.left &&
this.right == that.right

case that: Branch[t] =>

case that: Branch[_] =>

// In file equality/Tree3.scala

override def hashCode: Int =
41 * (
41 * (
41 + elem.hashCode
) + left.hashCode
) + right.hashCode

// In file equality/Tree3.scala

def canEqual(other: Any) = other match {
case that: Branch[_] => true
case _ => false
}

// In file equality/Tree3.scala

def canEqual(other: Any) = other.isInstanceOf[Branch[_]]

// In file equality/Tree4.scala

class Branch[T](
val elem: T,
val left: Tree[T],
val right: Tree[T]
) extends Tree[T] {

override def equals(other: Any) = other match {
case that: Branch[_] => (that canEqual this) &&
this.elem == that.elem &&
this.left == that.left &&
this.right == that.right
case _ => false
}

def canEqual(other: Any) = other.isInstanceOf[Branch[_]]

override def hashCode: Int =
41 * (
41 * (
41 + elem.hashCode
) + left.hashCode
) + right.hashCode
}

```

### 30.4 Recipes for equals and hashCode

```
// In file equality/Rational.scala

class Rational(n: Int, d: Int) {

require(d != 0)

private val g = gcd(n.abs, d.abs)
val numer = (if (d < 0) -n else n) / g
val denom = d.abs / g

private def gcd(a: Int, b: Int): Int =
if (b == 0) a else gcd(b, a % b)

override def equals(other: Any): Boolean =
other match {

case that: Rational =>
(that canEqual this) &&
numer == that.numer &&
denom == that.denom

case _ => false
}

def canEqual(other: Any): Boolean =
other.isInstanceOf[Rational]

override def hashCode: Int =
41 * (
41 + numer
) + denom

override def toString =
if (denom == 1) numer.toString else numer +"/"+ denom
}

def canEqual(other: Any): Boolean =

other.isInstanceOf[Rational]

override def equals(other: Any): Boolean =

other match {
// ...
}

case that: Rational =>

super.equals(that) &&

(that canEqual this) &&

numer == that.numer &&
denom == that.denom

case _ => false

override def hashCode: Int =
41 * (
41 * (
41 * (
41 * (
41 + a.hashCode
) + b.hashCode
) + c.hashCode
) + d.hashCode
) + e.hashCode

override def hashCode: Int =
41 * (
41 + numer
) + denom

override def hashCode: Int =
41 * (
41 * (
super.hashCode
) + numer
) + denom

override val hashCode: Int =
41 * (
41 + numer
) + denom

```