2011-05-03 12 views
49

Ho letto che il costrutto Scala'a case class genera automaticamente un'implementazione equals e hashCode. Come si presenta esattamente il codice generato?hashCode nelle classi case in Scala

+1

possibile duplicato (http://stackoverflow.com/ domande/4526706/what-code-is-generated-for-an-equals-hashcode-method-of-a-case-class) – Suma

risposta

67

Come diceva il mio professore, solo il codice dice la verità! Quindi, basta dare un'occhiata al codice che viene generato per:

case class A(i: Int, s: String) 

Siamo in grado di istruire il compilatore Scala per mostrarci il codice generato dopo le varie fasi, qui dopo coontrollore dei tipo:

% scalac -Xprint:typer test.scala 
[[syntax trees at end of typer]]// Scala source: test.scala 
package <empty> { 
    @serializable case class A extends java.lang.Object with ScalaObject with Product { 
    .. 
    override def hashCode(): Int = ScalaRunTime.this._hashCode(A.this); 
    ... 
    override def equals(x$1: Any): Boolean = A.this.eq(x$1).||(x$1 match { 
     case (i: Int,s: String)A((i$1 @ _), (s$1 @ _)) if i$1.==(i).&&(s$1.==(s)) => x$1.asInstanceOf[A].canEqual(A.this) 
     case _ => false 
    }); 


    override def canEqual(x$1: Any): Boolean = x$1.$isInstanceOf[A]() 
    }; 
} 

Così è possibile vedere che il calcolo del codice hash è delegato a ScalaRunTime._hashCode e l'uguaglianza dipende dall'uguaglianza dei membri della classe case.

+4

Questo non solo lo spiega bene, ma mi ha insegnato su '-Xprint: typer'. Molte grazie! L'unica cosa di cui sono confuso è cosa significa "ScalaRunTime.this"? Perché non semplicemente 'ScalaRunTime._hashCode'? –

+0

La sintassi 'ClassName.this' viene in genere utilizzata per accedere a un' this' esterno da una classe interna (che è la stessa di Java). Non sono sicuro del motivo per cui è stampato qui, forse è solo il modo in cui il codice è stampato dal compilatore. Ma questa è solo una supposizione, qualcun altro? –

46

Il generato hashCode solo chiamate scala.runtime.ScalaRunTime._hashCode, che è definito come:

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

Quindi, quello che si ottiene è elem1 * 41**n + elem2 * 41**(n-1) .. elemn * 1, dove n è l'arietà della classe caso e elemi sono i membri di quella classe caso.

+0

Grazie per la risposta chiara. Ora non so se dovrei accettare la tua risposta o la risposta di Mirko, dalla quale ho anche imparato il pratico trucco '-Xprint: typer' ... –

+0

Insieme, entrambe le risposte stanno rispondendo perfettamente alla domanda :-) –

0

Sembra che le cose siano cambiate; usando l'esempio di Mirko case class A(i: Int, s: String) ottengo: [? Qual è il codice generato per un metodo equals/hashCode di una classe case]

override <synthetic> def hashCode(): Int = { 
     <synthetic> var acc: Int = -889275714; 
     acc = scala.runtime.Statics.mix(acc, i); 
     acc = scala.runtime.Statics.mix(acc, scala.runtime.Statics.anyHash(s)); 
     scala.runtime.Statics.finalizeHash(acc, 2) 
    }; 

e

override <synthetic> def equals(x$1: Any): Boolean = A.this.eq(x$1.asInstanceOf[Object]).||(x$1 match { 
    case (_: A) => true 
    case _ => false 
}.&&({ 
     <synthetic> val A$1: A = x$1.asInstanceOf[A]; 
     A.this.i.==(A$1.i).&&(A.this.s.==(A$1.s)).&&(A$1.canEqual(A.this)) 
    })) 
    };