0

I have:

sealed trait BEValue

case class BEByteString(val value: Array[Byte]) extends BEValue { 
  def equals(that: BEByteString): Boolean = this.value.deep == that.value.deep
  def ==(that: BEByteString) = this equals that
}

case class BEList(val value: List[BEValue]) extends BEValue

BEByteString("spam".getBytes) == BEByteString("spam".getBytes) //true

val l1 =  BEList(BEByteString("spam".getBytes):: Nil)
val l2 =  BEList(BEByteString("spam".getBytes):: Nil)

l1 == l2 // false. Why ?
Jamil
  • 2,150
  • 1
  • 19
  • 20
  • You can omit the `val` keyword in case classes. It is added by default. – Marius Danila Jan 26 '14 at 23:53
  • I just posted an answer on another question which is a more detailed answer to your question. It is crucial for instances of your (case) class to correctly implement both equals and hashCode to properly behave within the Scala collections library. Or suffer unexpected failures. stackoverflow.com/a/56509518/501113 – chaotic3quilibrium Jun 10 '19 at 19:21

1 Answers1

4

You should not create your own == method. In scala == method is implemented using equals.

You should use override keyword to override equals method.

case class BEByteString(val value: Array[Byte]) extends BEValue { 
  override def equals(that: BEByteString): Boolean = this.value.deep == that.value.deep
}
// <console>:11: error: method equals overrides nothing
//          override def equals(that: BEByteString): Boolean = this.value.deep == that.value.deep
//                       ^       

You have created a new equals method. And your new == method is implemented using your new equals method. But case class equals is implemented using Any#equals method for parameters.

Any#equals method signature is def equals(that: Any): Boolean

case class BEByteString(val value: Array[Byte]) extends BEValue { 
  override def equals(that: Any): Boolean = that match {
    case BEByteString(thatValue) => thatValue.deep == this.value.deep
    case _ => false
  }
}

l1 == l2 
// Boolean = true

Note that you could use IndexedSeq[Byte] instead of Array[Byte]. In this case you could use a default equals implementation. It also makes your BEByteString class immutable: with Array[Byte] one can change a content of value.

senia
  • 37,745
  • 4
  • 88
  • 129
  • Just a quick tip: `java.util.Arrays.equals(this.value, that.value)` is probably faster than `this.value.deep == that.value.deep` – Marius Danila Jan 26 '14 at 23:50
  • And I thought Scala was a typesafe langauge ;) – Jamil Jan 26 '14 at 23:53
  • @Jamil: It's typesafe, but it also allows method overloading. – senia Jan 26 '14 at 23:57
  • I am talking about `that: Any`. Which is to say that l1.equals("2") is not a compile time error but rather an isInstanceOf/asInstanceOf cast. – Jamil Jan 27 '14 at 00:01
  • @Jamil: `that: Any` in `equals` is inherited from `java`. If you want to get a typesafe equality take a look at [scalaz/Equal](http://eed3si9n.com/learning-scalaz/Equal.html). – senia Jan 27 '14 at 00:01
  • @Jamil: Note that you could use `IndexedSeq[Byte]` instead of `Array[Byte]`. In this case you could use a default `equals` implementation. – senia Jan 27 '14 at 00:05
  • 1
    additionally, per http://stackoverflow.com/questions/7370925/what-is-the-standard-idiom-for-implementing-equals-and-hashcode-in-scala , you'll want to define `hashCode` as well. see http://www.artima.com/lejava/articles/equality.html – Rob Starling Jan 27 '14 at 01:32