Gre*_*idt 27 scala initialization lazy-evaluation
请考虑以下代码段:
object A {
val b = c
val c = "foo"
}
println( A.b ) // prints "null"
Run Code Online (Sandbox Code Playgroud)
作为更大程序的一部分,这将导致运行时失败.编译器显然允许从'b'到(未初始化)'c'的前向引用,但是'b'留下c的原始空值.为什么允许这样做?是否有可以从此功能中受益的编程方案?
将代码更改为直接序列并且行为更改:
val b = c
val c = "foo"
println( b ) // prints "foo"
Run Code Online (Sandbox Code Playgroud)
为什么行为不同?为什么这甚至有效?谢谢.
更新1:
问题出现了我如何运行第二个例子.我简化了设置并使用最新的Scala插件在IntelliJ IDEA 10.5.2中使用Scala 2.9.0.1编译它.这是确切的代码,在一个新创建的和其他空项目中,我用它来测试它,它在这种环境中编译并运行良好:
package test
object Main {
def main( args: Array[String] ) {
val b = c
val c = "foo"
println( b ) // prints "foo"
}
}
Run Code Online (Sandbox Code Playgroud)
对于它的价值,IDEA还认为(当我点击"通过"对val b = c中的'c'的引用时)我指的是(c)的(后面)声明.
Dan*_*ral 18
类或对象的主体是主要构造函数.构造函数与方法一样,是按顺序执行的一系列语句 - 要做其他任何事情,它必须是一种非常不同的语言.我很确定你不希望Scala以顺序执行任何其他顺序的方法语句.
这里的问题是类和对象的主体也是成员的声明,这是你混淆的根源.您可以看到val
声明正是:一种声明式编程形式,如Prolog程序或XML配置文件.但它们实际上是两件事:
// This is the declarative part
object A {
val b
val c
}
// This is the constructor part
object A {
b = c
c = "foo"
}
Run Code Online (Sandbox Code Playgroud)
您问题的另一部分是您的示例非常简单.这是一种特殊情况,其中某种行为似乎有意义.但考虑如下:
abstract class A {
def c: String
}
class B extends A {
val b = c
override val c = "foo"
}
class C extends { override val c = "foobar" } with B
val x = new C
println(x.b)
println(x.c)
Run Code Online (Sandbox Code Playgroud)
你期望发生什么?构造函数执行的语义保证了两件事:
输出:
它将打印"foobar"两次更多=> https://docs.scala-lang.org/tutorials/FAQ/initialization-order.html