我正在阅读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关键字只会延迟初始化?在这里看来,即使在声明之前,这些值在某种程度上也在范围内,从不介意初始化......
希望有人能够解释这里发生了什么!
答案 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)在包含类/对象的任何部分中都在“范围内”,无论它在何处被声明。
也就是说,对于任何引用它并在声明之前运行的代码,它不会被“初始化”。