Ruby是纯面向对象语言,1,1.0这些基本类型在Ruby中也是对象。如:
> 1.class
=> Fixnum
> "hello".class
=> String
> 1.0.class
=> Float
Scala 也是面向对象(与函数式结合的)语言。在Scala 2.9.0 下, 如果我们试图 1.getClass,期待会返回java.lang.Class[Int],实际上却报错了:
解读一下编译器提示的错误:
1 是基本类型, 基本类型(值类型AnyVal)没有定义getClass方法,只有引用类型(AnyRef)定义,可以强制进行装箱,通过x.asInstanceOf[AnyRef]的方式:
好吧, 按照提示做一遍:
scala> 1.asInstanceOf[AnyRef].getClass
res0: java.lang.Class[_ <: AnyRef] = class java.lang.Integer
好像是那么回事了, 通过这一篇介绍的方法:scalac -Xprint:cleanup X.scala,找到 1.asInstanceOf[AnyRef].getClass对应抽象语法树AST是:
扣源代码,调用了Int伴生对象的box方法,定义如下:
object Int extends AnyValCompanion { ... def box(x: Int): java.lang.Integer = java.lang.Integer.valueOf(x) ... }
可见,box的作用很明确,将Int的装箱为java.lang.Integer。
到这里,好像一切都顺理成章了。但是仔细想一想,scala.Predef 定义了Int到java.lang.Integer隐式转换函数:
这里1.getClass方法为什么不通过这个隐式转换后调用呢? 试试java.lang.Integer.intValue()方法实施到1上,看什么效果:
scala> 1.intValue()
res1: Int = 1
有点奇怪吧?? 试了一下,Scala 2.8 版也有此问题。
难道对Scala对基本类型调用getClass 做了特别处理?(这里还没想清楚,求解释)
最近(2011-07-25)Scala发布了 2.9.1 RC1版, 推上有人提到 1.getClass这个问题。马上试试:
$ scala Welcome to Scala version 2.9.1.RC1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_26). scala> 1.getClass res0: java.lang.Class[Int] = int scala> (1.0).getClass res1: java.lang.Class[Double] = double scala> true.getClass res2: java.lang.Class[Boolean] = boolean
看到没, 如期望的一样,对基本类型调用getClass, 能正常的返回其对应的类Class信息了。
到底Scala 2.9.1 做什么什么改进呢?
经过一番探寻, 得知:
Any根类新定义了getClass这个抽象方法方法,看看以下类型体系:
Any
├── AnyRef
└── AnyVal
├── Boolean (scala.Boolean)
├── Char
├── Int
└── Unit
明显的, Int类实现了这个getClass方法, 不用去做装箱或隐式转换Magic处理了。
这样处理是很自然的, 也觉得Scala 2.9.1面向对象变得更“纯”了。
(注:Any是“编译期类型”,看不到源码的。通过文档看看scala2.9.0跟Scala主干的对比吧:
http://www.scala-lang.org/api/current/index.html#scala.Any
http://www.scala-lang.org/archives/downloads/distrib/files/nightly/docs/library/index.html#scala.Any)