In the scaladoc of scala.Any
, the operator ==
(or, method ==
) is explained:
The expression
x == that
is equivalent toif (x eq null) that eq null else x.equals(that)
http://www.scala-lang.org/api/current/#scala.Any
For objects of subclasses of AnyRef
, I can understand it easily, and I didn't see any strange things.
However, for values of AnyVal
, (I mean Int
, Double
, Long
, and so on,) the above definition is somewhat tricky (1 eq null
? This does not compile if we do not convert 1
to java.lang.Integer). Also, ==
and equals()
behave differently.
I'll give some examples.
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
So far, nothing is strange. But if we do the same things with equals()
methods,
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
So if the types are different, equals() always returns false, whereas == tests if they represent the same value if they are converted to the same type.
In the case of subclass of AnyRef
, methods ==
and equals()
return the same.
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
So, why methods ==
and equals()
are diffrent for AnyVal
?
I'm using Scala version 2.10.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_25).
EDIT 1
I saw that == cannot be overriden directly, as it is defined as a final method in class Any according to Programming in Scala, 2nd Edition.
EDIT 2
Although there is an answer, my question remains. I will leave this question open.
What correspond to scala.Int
and scala.Long
in Java are Java's primitive types int
and long
.
In Java, java.lang.Integer
and java.lang.Long
are classes, so their variables are references, which can have null
.
That means, they are like AnyRef
in Scala. Not AnyVal
.
Scala's AnyVal
- scala.Int
and scala.Long
cannot have null
values, neither can Java's int
and long
.
Also, java.lang.Integer
's ==
in Java is for reference equality (same as eq
in Scala).
What you get using java.lang.Integer
in Scala REPL will be quite different from what you get with it in pure Java Project with .java source file in this respect.
However, what I could get from using classes of primitive types in Java was: (THIS IS 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))));
}
}
output:
true false false true falseYes, they behave similar to scala AnyVal's
equals()
. But, then, why does this happen?
Does Scala's AnyVal
's ==
correspond to ==
of Java's primitive type
and does Scala's AnyVal's equals()
correspond to equals()
of Java's class types?
What about equality tests with BigInt? There is no corresponding primitive type in Java.
The question remains...
EDIT 3
I could find some information from scaladoc. (http://www.scala-lang.org/api/current/index.html#scala.Int)
The Implicit information from the item of Shadowed Implicit Value Members,
I could find ==
was overloaded for Char
, Short
, Float
, and ...,
and ==
will call implicit conversions int2double
, int2float
, or int2long
.
Whereas equals()
is only defined for Any
, and it will call implicit conversion int2Integer
.
That is, Int.equals()
will be the same as java.lang.Integer.equals()
.
One question remains:
Why ==
of AnyVal
is overloaded, and equals()
of AnyVal
is not overloaded?
== is a final method, and calls . equals , which is not final. This is radically different than Java, where == is an operator rather than a method and strictly compares reference equality for objects.
equals Method: The equals method used to tests value equality. if x equals y is true if both x and y have the same value. They do not need to refer to the identical instance.
The relevant discussions are the descriptive
spec for == from 2010
and the speculative
Rethinking equality from 2011
FWIW, the spec calls out equality for numeric value types in 12.2.
Or, in HTML. (Quote at bottom, below.)
In his "pidgin spec-ese" of 2010, Paul Phillips puts it this way:
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.
The spec doesn't speak of boxed primitives, aside from a passing reference in 12.5 to the conversions provided by Predef
. You're not generally meant to be aware of when a primitive is stored in its "boxed" form, unless of course you need to for performance reasons.
So, for example, these values are silently unboxed and promoted for you:
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
I'm kind of surprised that you note
2.0 == BigInt(2) // So far, nothing is strange.
To me, that's slightly magical. It calls into BoxesRunTime.equals
as described by 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
Here is the spec, for reference, which in this form basically just promises to do the right thing:
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
}
I expect this was done because of auto-boxing and a desire to stay consistent with expectations held over from Java, and maths in generel (1 = 1.0 = 1 (represented as a long) etc.). For example, running comparisons between Scala numeric types and Java numeric types:
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
Compared with:
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With