The following code can be compiled without errors.
val a: Int = 1
val b = a.asInstanceOf[AnyRef]
That makes me confused, because Int extends AnyVal which is not a subclass but a sibling of AnyRef.
However, if an ascription is used as following:
val a: Int = 1
val b: AnyRef = a
It doesn't work.
error: type mismatch;
found : Int
required: AnyRef
Note: an implicit exists from scala.Int => java.lang.Integer, but
methods inherited from Object are rendered ambiguous. This is to avoid
a blanket implicit which would convert any scala.Int to any AnyRef.
You may wish to use a type ascription: `x: java.lang.Integer`.
val b: AnyRef = a
What I am understanding :
asInstanceOf
is executed at run time, it forces compiler to believe val a is an AnyRef.
However, an ascription is at compile time, the conversion can not pass the type check, so we have a "type mismatch" error.
My questions:
This is because of autoboxing :
scala>val a: Int = 1
a: Int = 1
scala> a.getClass
res2: Class[Int] = int
scala> val b = a.asInstanceOf[AnyRef]
b: AnyRef = 1
scala> b.getClass
res1: Class[_ <: AnyRef] = class java.lang.Integer
by forcing the conversion to AnyRef
(java.lang.Object
) you trigger autoboxing from int to java.lang.Integer
If AnyRef is considered as
java.lang.Object
in JVM, how aboutAnyVal
? Is it an Object at run time ?
AnyRef
is indeed an alias for java.lang.Object
AnyVal
is a "virtual" type, it only exists at compile time for the sake of the type system completeness.
At run time instances extending AnyVal
are converted to the corresponding native type (int
, double
, etc) except for String
which goes to java.lang.String
which itself extends java.lang.Object
but has special handling in the JVM.
But
AnyVal
is sibling ofAnyRef
, isn't it ?)
Both AnyVal
and AnyRef
extend the type Any
but they don't extend each other.
Are there some tricks played by scala compiler ?
Loads :)
For a more complete explanation of the Scala type hierarcy, I suggest you start by reading : http://docs.scala-lang.org/tutorials/tour/unified-types.html
Supplementary to "because of autoboxing", you can observe what magic is used.
-Xprint:all
will show which compiler phase has done the magic.
-Ytyper-debug
shows what decisions were made by typer.
For instance, given val a: Int
, then val b = a.isInstanceOf[AnyRef]
is changed to Int.box(a).$asInstanceOf[Object]
in erasure, where $asInstanceOf
is the nominal member of Object
.
There's a nice comment about these transforms in erasure:
/** Replace member references as follows:
*
* - `x == y` for == in class Any becomes `x equals y` with equals in class Object.
* - `x != y` for != in class Any becomes `!(x equals y)` with equals in class Object.
* - x.asInstanceOf[T] becomes x.$asInstanceOf[T]
* - x.isInstanceOf[T] becomes x.$isInstanceOf[T]
* - x.isInstanceOf[ErasedValueType(tref)] becomes x.isInstanceOf[tref.sym.tpe]
* - x.m where m is some other member of Any becomes x.m where m is a member of class Object.
* - x.m where x has unboxed value type T and m is not a directly translated member of T becomes T.box(x).m
* - x.m where x is a reference type and m is a directly translated member of value type T becomes x.TValue().m
* - All forms of x.m where x is a boxed type and m is a member of an unboxed class become
* x.m where m is the corresponding member of the boxed class.
*/
By contrast, the conversion due to the ascription mentioned in the OP's error message happens because of an implicit in Predef
:
scala> val I: java.lang.Integer = a
[[syntax trees at end of typer]] // <console>
private[this] val I: Integer = scala.this.Predef.int2Integer($line3.$read.$iw.$iw.a);
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