Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
636 views
in Technique[技术] by (71.8m points)

scala - Forward References - why does this code compile?

Consider this snippet:

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

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.

Update 1:

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"
    }
 }

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'.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.

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. Predictability. You might find it non-intuitive at first, but the rules are clear and relatively easy to follow.
  2. Subclasses can depend on superclasses having initialized themselves (and, therefore, its methods being available).

Output:

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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...