The relevant discussions are the descriptive
and the speculative
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
}