声明或定义引入的名称范围是 包含绑定的整个语句序列。但是,有一个 块中前向引用的限制:在语句序列中 如果
s[1]...s[n]
中的简单名称引用,则s[i]
构成一个块 由s[j]
j >= i
定义的实体,然后是所有s[k]
介于s[i]
和s[j]
之间,包括<{1}},
s[k]
不能是变量定义。- 如果
s[k]
是值定义,则必须为lazy
。
编辑:我不确定MikaëlMayer的答案是否真的解释了一切。考虑:
object Test {
def main(args: Array[String]) {
println(x)
lazy val x: Int = 6
}
}
这里,在代码中实际定义之前,必须先读取/评估惰性值x
!这与Mikaël声称懒惰评估消除了在定义之前评估事物的需要相矛盾。
答案 0 :(得分:6)
通常你不能拥有这个:
val e: Int = 2
val a: Int = b+c
val b: Int = c
val c: Int = 1
val d: Int = 0
因为在定义a时尚未定义值c。因为引用c,a和c之间的所有值都应该是惰性的,以便避免依赖
val e: Int = 2
lazy val a: Int = b+c
lazy val b: Int = c
lazy val c: Int = 1
val d: Int = 0
这实际上将a,b和c翻译为对象,其值在读取时被初始化,这将在声明之后,即这相当于:
val e: Int = 2
var a: LazyEval[Int] = null
var b: LazyEval[Int] = null
var c: LazyEval[Int] = null
a = new LazyEval[Int] {
def evalInternal() = b.eval() + c.eval()
}
b = new LazyEval[Int] {
def evalInternal() = c.eval()
}
c = new LazyEval[Int] {
def evalInternal() = 1
}
val d = 0
其中LazyEval
类似于以下内容(由编译器本身实现)
class LazyEval[T] {
var value: T = _
var computed: Boolean = false
def evalInternal(): T // Abstract method to be overriden
def eval(): T = {
if(computed) value else {
value = evalInternal()
computed = true
value
}
}
}
修改强>
val在java中并不存在。它们是局部变量或在计算中不存在。因此,lazy val的声明在任何事情发生之前就存在了。请记住,闭包是在Scala中实现的。 您的块将被重写为:
object Test { def main(args: Array[String]) { // Declare all variables, val, vars. var x: Lazy[Int] = null // No more variables to declare. Lazy/or not variable definitions x = new LazyEval[Int] { def evalInternal() = 6 } // Now the code starts println(x) } }
答案 1 :(得分:2)
您试图避免引用可证明未初始化(或可能未初始化)的实体。
在一个块中,赋值按源顺序发生,但在类模板中,可以提前覆盖和初始化成员。
例如,
{ val a = b ; val b = 1 } // if allowed, value of a is undefined
但在模板中
class X { val a = b ; val b = 1 } // warning only
val x = new { override val b = 2 } with X
x.a // this is 2
class Y(override val b: Int) extends X // similarly
您还想避免这种情况:
locally {
def a = c
val b = 2 // everything in-between must be lazy, too
def c = b + 1
}
本地对象与lazy vals明确相同:
{ object p { val x = o.y } ; object o { val y = 1 } }
其他类型的前向参考:
{ val x: X = 3 ; type X = Int }
该规范讨论了对&#34;实体&#34;的前向引用。 - &#34;名称是指实体&#34; - 其他地方意味着术语和类型,但显然它实际上只意味着这里的术语。
它会让你伤害自己:
{ def a: Int = b ; def b: Int = a; a }
也许你的自我毁灭模式必须明确定义。那好吧。