2013-08-28 8 views
12

Nel scaladoc del scala.Any, l'operatore == (o, il metodo ==) è spiegato:Perché == operatore ed equals() comportarsi in modo diverso per i valori di AnyVal a 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

Per gli oggetti di sottoclassi di AnyRef , Posso capirlo facilmente, e non ho visto nulla di strano.

Tuttavia, per i valori di AnyVal, (intendo Int, Double, Long, e così via,) la definizione di cui sopra è un po 'complicato (1 eq null? Questo non compila se non ci convertiamo 1 a java.lang.Integer). Inoltre, == e equals() si comportano diversamente.

Darò alcuni esempi.

 
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 

Finora, nulla è strano. Ma se facciamo le stesse cose con equals() metodi,

 
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 

Quindi, se i tipi sono diversi, equals() restituisce sempre false, mentre == test se rappresentano lo stesso valore se vengono convertiti allo stesso tipo .

Nel caso della sottoclasse di AnyRef, i metodi == e equals() restituiscono lo stesso.

 
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 

Quindi, perché i metodi == e equals() sono differenti per AnyVal?

Utilizzo la versione 2.10.2 di Scala (Java Server HotSpot (TM) a 64 bit, Java 1.7.0_25).

EDIT 1
ho visto che == non può essere sovrascritto direttamente, come è definito come un metodo final nella classe Qualunque secondo Programming in Scala, 2nd Edition.

EDIT 2
Anche se c'è una risposta, la mia domanda rimane. Lascerò questa domanda aperta.

Ciò che corrisponde a scala.Int e scala.Long in Java sono i tipi primitivi di Java int e long.
In Java, java.lang.Integer e java.lang.Long sono classi, quindi le loro variabili sono riferimenti, che possono avere null. Ciò significa che sono come AnyRef in Scala. Non AnyVal.
Scala AnyVal - scala.Int e scala.Long non possono avere valori , né Javae long.
Inoltre, lo == di in Java è per l'uguaglianza di riferimento (uguale a eq in Scala).
Quello che si ottiene usando java.lang.Integer in Scala REPL sarà molto diverso da quello che si ottiene con esso in puro Progetto Java con file di origine .java a questo riguardo.

Tuttavia, ciò che ho potuto ottenere di utilizzare classi di tipi primitivi in ​​Java è stato: (Questo è 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)))); 
    } 
} 

uscita:

 
true 
false 
false 
true 
false
Sì, si comportano simile alla Scala AnyVal di equals(). Ma, allora, perché succede?

Does AnyVal s' == di Scala corrispondono a == di tipo primitivo di Java
e fa della Scala AnyVal equals() corrispondono a equals() delle tipologie di classi di Java?
E i test di uguaglianza con BigInt? Non esiste un tipo primitivo corrispondente in Java.
La questione rimane ...

EDIT 3
sono riuscito a trovare alcune informazioni da scaladoc. (http://www.scala-lang.org/api/current/index.html#scala.Int)
Il informazioni implicite dalla voce di ombreggiato implicite di valore Utenti,
ho potuto trovare == era sovraccarica per Char, Short, Float, e ...,
e == chiamerà conversioni implicite int2double , int2float o int2long.
Mentre equals() è definito solo per Any e chiama la conversione implicita int2Integer.
Cioè, Int.equals() sarà lo stesso di java.lang.Integer.equals().

Una domanda rimane:
Perché == di AnyVal è sovraccarico, e equals() di AnyVal non sovraccaricare?

+0

Mi dispiace, ma sono confuso dopo l'intero post. Puoi specificare alla fine di quale è la domanda esatta? – Jatin

+0

@Jatin Il metodo '==' e 'equals()' sono gli stessi nei valori di 'AnyRef' in Scala. Penso che dovrebbero essere uguali anche per i valori di 'AnyVal'. In realtà, sono diversi. Tuttavia, non ho trovato nulla a riguardo mentre stavo studiando Scala. Quindi, perché '==' e 'equals()' non sono gli stessi per 'AnyVal'? C'è qualche specifica su questo? – Naetmul

+0

@Naetmul, che dire di questo output: 'println (Double.NaN == Double.NaN) println (Double.NaN uguale a Double.NaN) ', ero expectiong vero e vero ma l'output è falso vero, non capisco ogni aiuto sarà molto apprezzato !!! – Aamir

risposta

8

Le discussioni rilevanti sono il descrittiva

spec for == from 2010

e speculativa

Rethinking equality from 2011

FWIW, la specifica richiama l'uguaglianza per i tipi di valori numerici in 12.2.

Oppure, in HTML. (Citazione in basso, sotto.)

Nel suo "pidgin spec-ese" del 2010, Paul Phillips mette in questo modo:

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.

Le specifiche non parla di primitive in scatola, a parte un accenno nel 12,5 per le conversioni fornito da Predef. In genere non si intende essere consapevoli di quando un primitivo è memorizzato nella sua forma "scatolata", a meno che, ovviamente, non sia necessario per motivi di prestazioni.

Così, per esempio, questi valori sono in silenzio unboxed e promosso per voi:

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  

Sono un po 'sorpreso che si nota

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

Per me, questo è un po' magica. Chiama in BoxesRunTime.equals come descritto da Paul Phillips.

 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 

Ecco le specifiche, per riferimento, che in questa forma sostanzialmente promette solo per fare la cosa giusta:

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

Mi aspetto che questo sia stato fatto a causa dell'auto-boxing e del desiderio di rimanere coerente con le aspettative mantenute da Java, e la matematica in generel (1 = 1.0 = 1 (rappresentato come una lunga) ecc.). Ad esempio, l'esecuzione confronti tra i tipi numerici Scala e Java tipi numerici:

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 

Rispetto:

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

Nel vero Java, sia 'new java.lang.Long (3L)' e 'new java.lang.Integer (3)' sono riferimenti. Inoltre, 'java.lang.Long' non può essere lanciato su' java.lang.Integer'; getterà 'java.lang.ClassCastException'. Se confronti questi riferimenti prima di convertirli nello stesso tipo, non verrà nemmeno compilato: genererà errore di compilazione: "tipi incomparabili: java.lang.Integer e java.lang.Long" Penso che 'java.lang.Long' sarà automaticamente convertito in 'scala.Long', e' java.lang.Integer' verrà automaticamente convertito in 'scala.Int'. Il codice sopra non funzionerà nel puro Java. – Naetmul

+0

Fare riferimento a http://www.coderanch.com/t/454301/java/java/Comparing-Integer-Long – Naetmul

Problemi correlati