3

我正在阅读 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 关键字只是延迟了初始化?在这里似乎这些值甚至在声明之前就在范围内,更不用说初始化了......

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

4

2 回答 2

7

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

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

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

换句话说:您可以对惰性 val 进行前向引用,但前提是它们之间没有 var 或非惰性 val。如果您认为惰性 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

在第一个示例中,Scalaa通过调用求值b,而后者又调用c,求值结果为6。但在下一个例子中,whenc不是懒惰的......

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

scala> o.a
res2: Int = 0

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

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

于 2013-08-02T06:26:49.553 回答
2

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

也就是说,对于任何引用它并在它的声明点之前运行的代码,它都不会被“初始化”。一个lazy-val 的代码不会在它被声明的时候运行,所以这是安全的。

于 2013-08-02T05:23:50.163 回答