为什么在Scala中可以在运行时将AnyVal转换为AnyRef?

时间:2014-09-19 10:17:41

标签: scala

可以编译以下代码而不会出错。

val a: Int = 1
val b = a.asInstanceOf[AnyRef]

这使我感到困惑,因为Int扩展了AnyVal,它不是子类,而是AnyRef的兄弟。

但是,如果按照以下方式使用归属:

val a: Int = 1
val b: AnyRef = a

它不起作用。

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

我的理解:

asInstanceOf在运行时执行,它迫使编译器相信val a是AnyRef。 但是,归档是在编译时,转换无法通过类型检查,因此我们遇到“类型不匹配”错误。

我的问题:

  1. 基本上,为什么转换在运行时有效?
  2. 如果在JVM中将AnyRef视为java.lang.Object,AnyVal怎么样?它是运行时的对象吗?
    • (如果是的话,它的类型是什么?java.lang.Object?但AnyVal是AnyRef的兄弟,不是吗?)
    • (如果没有,我们如何使用AnyVal的子类作为Object,如Int,Double)
    • scala编译器是否有一些技巧?

2 个答案:

答案 0 :(得分:5)

这是因为自动装箱:

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

通过强制转换为AnyRefjava.lang.Object),触发从int到java.lang.Integer的自动装箱

  

如果在JVM中将AnyRef视为java.lang.Object,那么AnyVal怎么样?   它是运行时的对象吗?

AnyRef确实是java.lang.Object的别名 AnyVal是一个&#34;虚拟&#34;类型,它只在编译时存在,以便类型系统完整。

在运行时,扩展AnyVal的实例将转换为相应的本机类型(intdouble等),String转到java.lang.String除外它本身扩展java.lang.Object但在JVM中有特殊处理。

  

但是AnyValAnyRef的兄弟姐妹,不是吗?)

AnyValAnyRef都扩展了Any类型,但它们并没有相互延伸。

  

scala编译器是否有一些技巧?

加载:)

有关Scala类型层次结构的更完整说明,建议您先阅读:http://docs.scala-lang.org/tutorials/tour/unified-types.html

答案 1 :(得分:4)

补充“因为自动装箱”,你可以观察到使用了什么魔法。

-Xprint:all将显示哪个编译器阶段完成了魔术。

-Ytyper-debug显示了由typer做出的决定。

例如,在给定val a: Int的情况下,val b = a.isInstanceOf[AnyRef]在擦除时更改为Int.box(a).$asInstanceOf[Object],其中$asInstanceOfObject的名义成员。

关于这些转换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.
 */

相比之下,OP的错误消息中提到的归因引起的转换是因为隐含在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);