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

Jus*_*ang 3 scope scala lazy-evaluation sbt

我正在阅读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"))
}
Run Code Online (Sandbox Code Playgroud)

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

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

Chr*_*tin 7

请参阅Scala语言规范的第4章:

声明或定义引入的名称范围是包含绑定的整个语句序列.但是,块中的前向引用存在限制:在组成块的语句序列s 1 ... s n中,如果s i中的简单名称是指由s j定义的实体,其中j≥i,则为s i和s j之间的所有s k,

  • s k不能是变量定义.
  • 如果s k是值定义,则它必须是惰性的.

换句话说:你可以在lazy vals上有前向引用,但前提是它们之间没有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
Run Code Online (Sandbox Code Playgroud)

在第一个示例中,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
Run Code Online (Sandbox Code Playgroud)

当Scala求值时a,它会调用b,它返回当前值c(当时是0JVM的整数默认值,因为c尚未初始化).然后 c在之后初始化,但到那时为时已晚.

另请参见:如何在Scala 2.10中实现惰性val类变量?