简体   繁体   English

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

[英]What are the scope rules for lazy vals in Scala?

I was reading the sbt documentation, and I came across this example in the section on multi project builds: 我正在阅读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"))
}

I am wondering how it is possible to reference the values foo and bar before they have been declared? 我想知道如何在声明值之前引用值foo和bar? I figure it has something to do with the lazy keyword, but from my reading, I thought the lazy keyword only delayed initialization? 我认为这与lazy关键字有关,但从我的阅读中,我认为lazy关键字只会延迟初始化? It seems here that the values are somehow in scope even before declaration, never mind initialization... 看来这里的值甚至在声明之前都在某种程度上,从不介意初始化......

Hopefully someone is able to explain what is going on here! 希望有人能够解释这里发生了什么!

See chapter 4 of the Scala language specification : 请参阅Scala语言规范的第4章:

The scope of a name introduced by a declaration or definition is the whole statement sequence containing the binding. 声明或定义引入的名称范围是包含绑定的整个语句序列。 However, there is a restriction on forward references in blocks: In a statement sequence s 1 ... s n making up a block, if a simple name in s i refers to an entity defined by s j where j ≥ i, then for all s k between and including s i and s j , 但是,对块中的前向引用存在限制:在构成块的语句序列s 1 ... s n中 ,如果s i中的简单名称是指由s j定义的实体,其中j≥i,则为s i和s j之间的所有s k

  • s k cannot be a variable definition. s k不能是变量定义。
  • If s k is a value definition, it must be lazy. 如果s k是值定义,则它必须是惰性的。

In other words: you can have forward references on lazy vals, but only if there are no vars or non-lazy vals between them. 换句话说:你可以在lazy vals上有前向引用,但前提是它们之间没有vars或非惰性val。 This is kind of intuitive if you consider that a lazy val works more like a method than like a variable, and walk though two quick examples on the REPL: 如果你认为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

In this first example, Scala evaluates a by invoking b , which in turn invokes c , which evaluates to 6 . 在第一个示例中,Scala通过调用b计算ab又调用c ,其计算结果为6 But in the next example, when c is not lazy ... 但是在下一个例子中,当c不是懒惰的时候......

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

scala> o.a
res2: Int = 0

When Scala evaluates a , it invokes b , which returns the current value of c (which at that time is 0 , the JVM's default value for integers, because c is not yet initialized). 当Scala计算a ,它调用b ,它返回c的当前值(当时为0 ,JVM是整数的默认值,因为c尚未初始化)。 Then c is initialized afterward, but by then it is too late. 然后 c被初始化,但到那时为时已晚。

See also: How are lazy val class variables implemented in Scala 2.10? 另请参见: 如何在Scala 2.10中实现惰性val类变量?

Just like in Java, a class instance variable (or scala val) is "in scope" within any part of the containing class/object, regardless of where it is declared. 就像在Java中一样,类实例变量(或scala val)在包含类/对象的任何部分内都是“在范围内”,而不管它在何处被声明。

That said, it will not be "initialized" for any code referencing it and running prior to it's point of declaration. 也就是说,对于任何引用它并在声明之前运行的代码,它不会被“初始化”。 A lazy-val's code doesn't run at the point it's declared, so that's safe. lazy-val的代码不会在声明它的位置运行,所以这是安全的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM