前向引用 - 为什么这段代码会编译? [英] Forward References - why does this code compile?

查看:33
本文介绍了前向引用 - 为什么这段代码会编译?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑这个片段:

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

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

As part of a larger program, this would lead to a failure at runtime. The compiler apparently permits the forward reference from 'b' to (uninitialized) 'c' but 'b' is left with c's original null value. Why is this permitted? Are there programming scenarios that would benefit from this feature?

将代码改为直线序列,行为改变:

Change the code to a straight sequence and the behavior changes:

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

为什么行为不同?为什么这甚至有效?谢谢.

Why is the behavior different? And why does this even work? Thanks.

更新 1:

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

The question came up how I ran the second example. I simplified the setup a bit and compiled it using Scala 2.9.0.1 inside IntelliJ IDEA 10.5.2 with the latest Scala plugin. Here is the exact code, in a freshly created and otherwise empty project, which I am using to test this, which compiles and runs fine in this environment:

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

就其价值而言,IDEA 还认为(当我单击通过"val b = c 中对c"的引用时)我指的是(后来的)c"声明.

For what it's worth, IDEA also thinks (when I click "through" the reference to 'c' in val b = c) that I am referring to the (later) declaration of 'c'.

推荐答案

类或对象的主体是主要构造函数.构造函数,就像方法一样,是按顺序执行的语句序列——要执行其他任何操作,它必须是一种非常不同的语言.我很确定您不希望 Scala 以除顺序之外的任何其他顺序执行您的方法的语句.

The body of a class or an object is the primary constructor. A constructor, like a method, is a sequence of statements that are executed in order -- to do anything else, it would have to be a very different language. I'm pretty sure you wouldn't like for Scala to execute the statements of your methods in any other order than sequential.

这里的问题是类和对象的主体也是成员的声明,这就是你混淆的根源.您可以看到 val 声明正是这样:一种声明性的编程形式,如 Prolog 程序或 XML 配置文件.但它们实际上是两件事:

The problem here is that the body of classes and objects are also the declaration of members, and this is the source of your confusion. You see val declarations as being precisely that: a declarative form of programming, like a Prolog program or an XML configuration file. But they are really two things:

// This is the declarative part
object A {
  val b
  val c
}

// This is the constructor part
object A {
  b = c
  c = "foo"
}

您的问题的另一部分是您的示例非常简单.这是一种特殊情况,其中某种行为似乎有意义.但请考虑以下内容:

Another part of your problem is that your example is very simple. It is a special case in which a certain behavior seems to make sense. But consider something like:

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)

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

What do you expect to happen? The semantics of constructor execution guarantees two things:

  1. 可预测性.一开始您可能会觉得它不直观,但规则清晰且相对容易遵循.
  2. 子类可以依赖于已经初始化的超类(因此,它的方法可用).

输出:

它会打印两次foobar"以获得更多 => https://docs.scala-lang.org/tutorials/FAQ/initialization-order.html

it will print "foobar" twice for more => https://docs.scala-lang.org/tutorials/FAQ/initialization-order.html

这篇关于前向引用 - 为什么这段代码会编译?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆