gpt4 book ai didi

scala - 前向引用 - 为什么这段代码可以编译?

转载 作者:行者123 更新时间:2023-12-02 09:05:38 25 4
gpt4 key购买 nike

考虑这个片段:

 object A {
val b = c
val c = "foo"
}
println( A.b ) // prints "null"

作为较大程序的一部分,这会导致运行时失败。编译器显然允许从“b”到(未初始化的)“c”的前向引用,但“b”保留了 c 的原始空值。为什么这是允许的?是否存在可以从此功能中受益的编程场景?

将代码更改为直接序列,行为会发生变化:

 val b = c
val c = "foo"
println( b ) // prints "foo"

为什么行为不同?为什么这会起作用?谢谢。

更新1:

问题出现了我如何运行第二个示例。我稍微简化了设置,并使用 IntelliJ IDEA 10.5.2 中的 Scala 2.9.0.1 和最新的 Scala 插件对其进行了编译。这是在一个新创建的空项目中的确切代码,我用它来测试它,它在此环境中编译并运行良好:

 package test
object Main {
def main( args: Array[String] ) {
val b = c
val c = "foo"
println( b ) // prints "foo"
}
}

就其值(value)而言,IDEA 还认为(当我单击“通过”val b = c 中对“c”的引用时)我指的是“c”的(稍后)声明。

最佳答案

类或对象的主体是主要构造函数。构造函数与方法一样,是按顺序执行的一系列语句——要执行其他操作,它必须是一种非常不同的语言。我很确定您不希望 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"
}

你的问题的另一部分是你的例子非常简单。这是一种特殊情况,其中某种行为似乎是有意义的。但考虑一下这样的事情:

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)

你预计会发生什么?构造函数执行的语义保证了两件事:

  1. 可预测性。一开始您可能会觉得它不直观,但规则很明确并且相对容易遵循。
  2. 子类可以依赖于已初始化自身的父类(super class)(因此其方法可用)。

输出:

它将打印“foobar”两次以获取更多 => https://docs.scala-lang.org/tutorials/FAQ/initialization-order.html

关于scala - 前向引用 - 为什么这段代码可以编译?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7762838/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com