Scala中lazy vals的范围规则是什么?

时间:2013-08-02 05:07:33

标签: scala scope sbt lazy-evaluation

我正在阅读sbt文档,我在多项目构建一节中遇到了这个例子:

import sbt._
import Keys._

object HelloBuild extends Build {
    lazy val root = Project(id = "hello",
                            base = file(".")) aggregate(foo, bar)

    lazy val foo = Project(id = "hello-foo",
                           base = file("foo"))

    lazy val bar = Project(id = "hello-bar",
                           base = file("bar"))
}

我想知道在声明值之前如何引用值foo和bar?我认为这与lazy关键字有关,但从我的阅读中,我认为lazy关键字只会延迟初始化?在这里看来,即使在声明之前,这些值在某种程度上也在范围内,从不介意初始化......

希望有人能够解释这里发生了什么!

2 个答案:

答案 0 :(得分:7)

Scala language specification的第4章:

  

声明或定义引入的名称范围是包含绑定的整个语句序列。但是,块中的前向引用存在限制:在语句序列中,s 1 ... s n 构成块,如果s i 是指由s j 定义的实体,其中j≥i,然后对于所有s k ,包括s i 和s j

     
      
  • s k 不能是变量定义。
  •   
  • 如果s k 是一个值定义,它必须是懒惰的。
  •   

换句话说:你可以在延迟val上有前向引用,但前提是它们之间没有vars或非惰性val。如果你认为lazy val更像是一个方法而不是变量,那么这很直观,并且在REPL上通过两个快速示例:

scala> object o { val a = b; lazy val c = 6; lazy val b = c }
defined module o

scala> o.a
res1: Int = 6

在第一个示例中,Scala通过调用a来评估b,而c又调用6,其评估为c。但在下一个例子中,当scala> object o { val a = b; val c = 6; lazy val b = c } defined module o scala> o.a res2: Int = 0 不是懒惰的时候......

a

当Scala评估b时,它会调用c,它返回0的当前值(当时为c,即JVM的默认值整数的值,因为c尚未初始化)。然后然后 {{1}}被初始化,但到那时已经太晚了。

另请参阅:How are lazy val class variables implemented in Scala 2.10?

答案 1 :(得分:2)

就像在Java中一样,类实例变量(或scala val)在包含类/对象的任何部分中都在“范围内”,无论它在何处被声明。

也就是说,对于任何引用它并在声明之前运行的代码,它不会被“初始化”。