24

I have some Java code which I'm translating to Scala.

The code consists of some immutable classes which would fit the purpose of a case class in Scala.

But I don't want to introduce bugs, therefore I want to be sure that the code being generated for equals and hashCode is/behaves equivalent to the current implementation.

I already looked in "Programming in Scala" but it only says

Third, the compiler adds “natural” implementations of methods toString, hashCode, and equals to your class.

soc
  • 27,983
  • 20
  • 111
  • 215

1 Answers1

45

Scala has a compiler option -Xprint:typer, which you can use to get the "post-typing source code that it uses internally".

scala -Xprint:typer -e 'case class Foo(a: String, b: Int)'

Here you see something like:

override def hashCode(): Int = ScalaRunTime.this._hashCode(Foo.this);
override def toString(): String = ScalaRunTime.this._toString(Foo.this);
override def equals(x$1: Any): Boolean = Foo.this.eq(x$1).||(x$1 match {
  case (a: String,b: Int)this.Foo((a$1 @ _), (b$1 @ _)) if a$1.==(a).&&(b$1.==(b)) => x$1.asInstanceOf[this.Foo].canEqual(Foo.this)
  case _ => false
});

But, this doesn't tell you how hashCode is generated. Here's the source for that:

def _hashCode(x: Product): Int = {
  var code = x.productPrefix.hashCode()
  val arr =  x.productArity
  var i = 0
  while (i < arr) {
    val elem = x.productElement(i)
    code = code * 41 + (if (elem == null) 0 else elem.hashCode())
    i += 1
  }
  code
}

And, in this example, the first case of the equals pattern matching would just be:

case that: Foo => this.a == that.a && this.b == that.b
Bradford
  • 4,143
  • 2
  • 34
  • 44
  • 15
    This shows how useful the Product trait is. The compiler doesn't have to generate a custom hashCode (or toString) for every case class. Instead, it can iterate over the "elements" of the object in a very generic fashion. That is, it combines the benefits of storing state in an object and storing it in a Map. i.e., generic operations over the collection. Clojure's defrecord form provides similar benefits. – Dean Wampler Dec 24 '10 at 15:53
  • 2
    Interesting. That means that if a case class has primitive members, they're going to have to be boxed whenever hashCode is called. – Erik Engbrecht Dec 27 '10 at 12:57
  • 3
    As of scala 2.9, the hashCode generated for case classes uses MurmurHash. – Alexander Poluektov Nov 11 '16 at 15:00