scala雾中风景(7): val x:Int = x + 1 的问题

前些天在scala中国的QQ群里,看到有人问为什么scala允许这样定义一个变量:

val x:Int = x + 1

这种在java里不允许,因为java里初始化一个变量(赋值)时不允许引用自身。java同样不允许下面的形式:

public class A {
    int x = y + 1;  //java中不行
    int y = 2;
}

必须把y定义在x前面,才可以在x初始化时使用y。而scala里可以:

scala> class A { 
            val x = y + 1 //scala里可以
            val y = 2 
       }

编译时只是给出警告,但会通过,运行也通过,只是x结果可能不符合你的预期;-)。

我们看一下为什么scala里会允许这种直觉上不合理的方式。看看scala在定义了一个val变量时,是怎样转成java字节码的

class A  {
  val x:Int =  1
  println(x) //看看访问x变量会被翻译成什么
}

对上面的代码通过 scalac -Xprint:jvm 可以看到:

class A extends Object {

    private[this] val x: Int = _;
    <stable> <accessor> def x(): Int = A.this.x;

    def <init>(): A = {
        A.super.<init>();
        A.this.x = 1;
        scala.this.Predef.println(scala.Int.box(A.this.x())); //调用的是x()方法而不是x变量
        ()
    }
}

它除了定义了一个 Int 类型的x变量,还定义了一个同名的 x() 方法,并且在其他地方访问x变量时实际都是调用的x()方法,它很像C#里的“属性”,C#里访问一个属性,都是调用的它的get方法。

所以scala里访问变量时,都是访问其同名的get方法;另参考这篇:scala中的无参方法与统一访问原则

现在清楚了,val x = y + 1 里面的y实际上是调用的 y()方法,val x:Int = x + 1 也是同理。不过这里面还有初始化的问题,scala的初始化比java的花样多一些,比如一个类在混入trait时的初始化顺序,以及引入了early definition的情况;后边再单独说。

scala雾中风景(7): val x:Int = x + 1 的问题》上有3条评论

  1. 廖师虎

    val x: Int = x + 1能通过编译,但val x = x + 1 会提示error: recursive value x needs type,看来指定类型是找到默认初值。

    回复
    1. hongjiang 文章作者

      是的,这种情况下不指定类型的话,无法推断出它的类型。

      回复

发表评论

电子邮件地址不会被公开。 必填项已用*标注