2013-08-28 13 views
12

Im scaladoc von scala.Any der Betreiber == (oder Methode ==) erklärt:Warum Operator == und equals() verhalten sich anders für Werte von AnyVal in Scala

The expression x == that is equivalent to if (x eq null) that eq null else x.equals(that) http://www.scala-lang.org/api/current/#scala.Any

Für Objekte von Subklassen von AnyRef Ich kann es leicht verstehen, und ich habe keine merkwürdigen Dinge gesehen.

jedoch für Werte von AnyVal, (ich meine Int, Double, Long, und so weiter,) die obige Definition etwas schwierig ist (1 eq null? Diese nicht kompiliert, wenn wir 1 konvertieren nicht zu java.lang.Integer). Auch == und equals() verhalten sich anders.

Ich werde einige Beispiele geben.

 
scala> 1 == 1 
res0: Boolean = true 

scala> 1 == 1.0 
res1: Boolean = true 

scala> 1 == 1.2 
res2: Boolean = false 

scala> 2 == BigInt(2) 
res3: Boolean = true 

scala> 2.0 == BigInt(2) 
res4: Boolean = true 

scala> 2 == BigInt(3) 
res5: Boolean = false 

Bis jetzt ist nichts seltsam. Aber wenn wir die gleichen Dinge tun mit equals() Methoden,

 
scala> 1 equals 1 
res7: Boolean = true 

scala> 1 equals 1.0 
res8: Boolean = false 

scala> 1 equals 1.2 
res9: Boolean = false 

scala> 2 equals BigInt(2) 
res10: Boolean = false 

scala> 2.0 equals BigInt(2) 
res11: Boolean = false 

scala> 2 equals BigInt(3) 
res12: Boolean = false 

Also, wenn die Typen unterschiedlich sind, gleich() immer false zurück, während == Tests, wenn sie den gleichen Wert darstellen, wenn sie auf die gleiche Art umgewandelt werden .

Im Fall der Unterklasse AnyRef geben die Methoden == und equals() dasselbe zurück.

 
scala> BigInt(2) == 2 
res25: Boolean = true 

scala> BigInt(2) == 2.0 
res26: Boolean = true 

scala> BigInt(3) == 2 
res27: Boolean = false 

scala> BigInt(2) equals 2 
res28: Boolean = true 

scala> BigInt(2) equals 2.0 
res29: Boolean = true 

scala> BigInt(3) equals 2 
res30: Boolean = false 

Also, warum Methoden == und equals() diffrent sind für AnyVal?

Ich benutze Scala Version 2.10.2 (Java HotSpot (TM) 64-Bit Server VM, Java 1.7.0_25).

EDIT 1
ich sah, dass == nicht direkt überschrieben werden kann, wie es für alle als eine endgültige Methode in der Klasse definiert ist gemäß Programming in Scala, 2nd Edition.

BEARBEITEN 2
Obwohl es eine Antwort gibt, bleibt meine Frage. Ich werde diese Frage offen lassen.

Was in Java scala.Int und scala.Long entspricht, sind Javas primitive Typen int und long.
In Java, java.lang.Integer und java.lang.Long sind Klassen, so dass ihre Variablen Referenzen sind, die null haben können. Das heißt, sie sind wie AnyRef in Scala. Nicht AnyVal.
Scala's AnyVal - scala.Int und scala.Long können keine null Werte haben, weder Java int noch long.
Auch java.lang.Integer== in Java ist für die Referenzgleichheit (das gleiche wie in Scala).
Was Sie mit Hilfe von java.lang.Integer in Scala REPL erhalten, wird sich von dem unterscheiden, was Sie damit in reinem Java-Projekt mit .java-Quelldatei in dieser Hinsicht bekommen.

Was ich jedoch von der Verwendung Klassen von primitiven Typen in Java bekommen konnte, war: (dies ist JAVA)

class Main { 
    public static void main(String[] args) { 
     System.out.println(String.valueOf(new java.lang.Integer(1).equals(1))); 
     System.out.println(String.valueOf(new java.lang.Integer(1).equals(1L))); 
     System.out.println(String.valueOf(new java.lang.Integer(1).equals(1.0))); 
     System.out.println(String.valueOf(new java.lang.Integer(1).equals(new java.lang.Integer(1)))); 
     System.out.println(String.valueOf(new java.lang.Integer(1).equals(new java.lang.Long(1)))); 
    } 
} 

Ausgabe:

 
true 
false 
false 
true 
false
Ja, sie zu scala AnyVal des equals() ähnlich verhalten. Aber warum passiert das?

Ist Scala AnyVal ‚s ==-== von Java primitivem Typ entsprechen
und hat Scala AnyVal des equals() entspricht equals() von Java-Klasse-Typen?
Was ist mit Gleichheitsprüfungen mit BigInt? Es gibt keinen entsprechenden primitiven Typ in Java.
Es bleibt die Frage ...

EDIT 3
ich einige Informationen von scaladoc finden konnten. (http://www.scala-lang.org/api/current/index.html#scala.Int)
Die Implizite Information aus dem Artikel von Beschattetes Implizite Wert Mitglieder,
ich == finden konnte, war für Char, überlastet Short, Float, und ...,
und == werden implizite Konvertierungen nennen int2double , int2float oder int2long.
Wobei equals() nur für Any definiert ist und die implizite Konvertierung int2Integer aufgerufen wird.
Das heißt, Int.equals() wird das gleiche sein wie java.lang.Integer.equals().

Eine Frage bleibt:
Warum == von AnyVal überlastet ist, und equals() von AnyVal nicht überlastet ist?

+0

Es tut mir leid, aber ich bin verwirrt, nach dem ganzen Beitrag. Können Sie bitte am Ende angeben, was genau die Frage ist? – Jatin

+0

@Jatin Die Methode '==' und 'equals()' sind in 'AnyRef'-Werten in Scala identisch. Ich denke, sie sollten auch für 'AnyVal'-Werte gleich sein. In der Tat sind sie anders. Ich konnte jedoch nichts darüber finden, als ich Scala studierte. Also, warum '==' und 'equals()' sind nicht die gleichen für 'AnyVal'? Gibt es dazu eine Spezifikation? – Naetmul

+0

@Naetmul, was ist mit dieser Ausgabe: 'println (Double.NaN == Double.NaN) println (Double.NaN gleich Double.NaN) ', ich war expectiong wahr und wahr, aber die Ausgabe ist falsch wahr, verstehe nicht es, jede Hilfe wird sehr geschätzt werden !!! – Aamir

Antwort

8

Die entsprechenden Diskussionen der beschreibende

spec for == from 2010

und die spekulative

sind

Rethinking equality from 2011

FWIW, die Spezifikation ruft die Gleichheit für numerische Werttypen auf in 12.2.

Oder, in HTML. (Zitat unten, unten.

)

In seinem "Pidgin spec-ese" von 2010, Paul Phillips drückt es so aus:

comparing two primitives (boxed or unboxed) with == should always give the result you would have gotten by comparing those values as unboxed primitives. When you call equals directly, you are skipping all that softening logic and instead treated to java's theory that two boxed values of different types are always unequal.

Die Spezifikation spricht nicht von boxed Primitiven, abgesehen von einer vorübergehenden Bezugnahme in 12.5 auf die Conversions bereitgestellt von Predef. Sie sollten sich nicht bewusst sein, wann ein Primitiv in seiner "boxed" Form gespeichert wird, es sei denn, Sie müssen dies aus Leistungsgründen tun.

So zum Beispiel sind diese Werte leise unboxed und für Sie befördert:

scala> val ds = List(7.0) 
ds: List[Double] = List(7.0) 

scala> val is = List(7) 
is: List[Int] = List(7) 

scala> ds(0) == is(0) 
res24: Boolean = true 

scala> :javap - 
    Size 1181 bytes 
    MD5 checksum ca732fd4aabb301f3ffe0e466164ed50 
    Compiled from "<console>" 
[snip] 
    9: getstatic  #26     // Field .MODULE$:L; 
    12: invokevirtual #30     // Method .ds:()Lscala/collection/immutable/List; 
    15: iconst_0  
    16: invokevirtual #36     // Method scala/collection/immutable/List.apply:(I)Ljava/lang/Object; 
    19: invokestatic #42     // Method scala/runtime/BoxesRunTime.unboxToDouble:(Ljava/lang/Object;)D 
    22: getstatic  #47     // Field .MODULE$:L; 
    25: invokevirtual #50     // Method .is:()Lscala/collection/immutable/List; 
    28: iconst_0  
    29: invokevirtual #36     // Method scala/collection/immutable/List.apply:(I)Ljava/lang/Object; 
    32: invokestatic #54     // Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I 
    35: i2d   
    36: dcmpl  

ich irgendwie bin überrascht, dass Sie

2.0 == BigInt(2) // So far, nothing is strange. 

Für mich beachten Sie, das ist etwas Magisches. Es ruft in BoxesRunTime.equals wie von Paul Phillips beschrieben. Hier

 9: ldc2_w  #22     // double 2.0d 
    12: invokestatic #29     // Method scala/runtime/BoxesRunTime.boxToDouble:(D)Ljava/lang/Double; 
    15: getstatic  #34     // Field scala/package$.MODULE$:Lscala/package$; 
    18: invokevirtual #38     // Method scala/package$.BigInt:()Lscala/math/BigInt$; 
    21: iconst_2  
    22: invokevirtual #44     // Method scala/math/BigInt$.apply:(I)Lscala/math/BigInt; 
    25: invokestatic #48     // Method scala/runtime/BoxesRunTime.equals:(Ljava/lang/Object;Ljava/lang/Object;)Z 

ist die Spezifikation, als Referenz, die in dieser Form im Grunde verspricht genau das Richtige zu tun:

The equals method tests whether the argument is a numeric value type. If this is true, it will perform the == operation which is appropriate for that type. That is, the equals method of a numeric value type can be thought of being defined as follows:

def equals(other: Any): Boolean = other match { 
    case that: Byte => this == that 
    case that: Short => this == that 
    case that: Char => this == that 
    case that: Int => this == that 
    case that: Long => this == that 
    case that: Float => this == that 
    case that: Double => this == that 
    case _ => false 
} 
2

Ich erwarte, dass dies getan wurde, weil Auto-Boxen und der Wunsch, mit den von Java gehaltenen Erwartungen und Mathe im Allgemeinen (1 = 1.0 = 1 (dargestellt als lang) etc.) übereinzustimmen. Zum Beispiel laufen Vergleiche zwischen Scala numerischen Typen und Java numerische Typen:

scala> val foo: Long = 3L 
foo: Long = 3 

scala> val bar: Int = 3 
bar: Int = 3 

scala> foo == bar 
res0: Boolean = true 

scala> foo.equals(bar) 
res1: Boolean = false 

Verglichen mit:

scala> val jfoo = new java.lang.Long(3L) 
jfoo: Long = 3 

scala> val jbar = new java.lang.Integer(3) 
jbar: Integer = 3 

scala> jfoo == jbar 
res2: Boolean = true 

scala> jfoo.equals(jbar) 
res3: Boolean = false 
+0

Im realen Java sind sowohl 'new java.lang.Long (3L)' als auch 'new java.lang.Integer (3)' Verweise. Darüber hinaus kann 'java.lang.Long' nicht in' java.lang.Integer' umgewandelt werden. Es wird 'java.lang.ClassCastException' geworfen. Wenn Sie diese Referenzen vor dem Konvertieren in den gleichen Typ vergleichen, wird es nicht einmal kompilieren: Es wird Kompilierungsfehler generieren: "unvergleichbare Typen: java.lang.Integer und java.lang.Long" Ich denke 'java.lang.Long' wird sein wird automatisch in 'scala.Long' konvertiert und' java.lang.Integer' wird automatisch in 'scala.Int' konvertiert. Der obige Code wird nicht im reinen Java ausgeführt. – Naetmul

+0

Siehe http://www.codeanch.com/t/454301/java/java/Comparing-Integer-Long – Naetmul