2012-08-29 3 views
18

Mögliche Duplizieren:
Scala: forward references - why does this code compile?Scala und Vorwärtsreferenzen

object Omg { 

    class A 

    class B(val a: A) 

    private val b = new B(a) 

    private val a = new A 

    def main(args: Array[String]) { 
    println(b.a) 
    } 

} 

der folgende Code druckt "null". In Java. Eine ähnliche Konstruktion wird wegen ungültiger Vorwärtsreferenz nicht kompiliert. Die Frage ist - warum lässt es sich in Scala gut übersetzen? Ist das per Design, in SLS beschrieben oder einfach in 2.9.1 Bug?

+1

Das Problem, das ärgert mich daran ist, dass es ein val seinen Wert zu ändern. Das macht mich traurig :-( – thoredge

+0

es ist ein bisschen seltsam - viele Fehler könnten dadurch verursacht werden, und ich verließ mich auf Java-Verhalten, das Werte initialisiert werden muss, bevor sie verwendet werden. – jdevelop

+1

@jdevelop Sogar Java fängt nicht alles ab mögliche Vorwärtsreferenzen –

Antwort

23

Es ist kein Fehler, sondern ein klassischer Fehler beim Lernen von Scala. Wenn das Objekt Omg initialisiert wird, werden zuerst alle Werte auf den Standardwert gesetzt (null in diesem Fall) und dann wird der Konstruktor (d. H. Der Objektkörper) ausgeführt.

, damit es funktioniert, fügen Sie einfach das lazy Schlüsselwort vor der Erklärung Sie vorwärts Referenzierung (Wert a in diesem Fall) sind:

object Omg { 

    class A 

    class B(val a: A) 

    private val b = new B(a) 

    private lazy val a = new A 

    def main(args: Array[String]) { 
    println(b.a) 
    } 
} 

Wert a wird dann bei Bedarf initialisiert werden.

Diese Konstruktion ist schnell (die Werte werden nur einmal für die gesamte Anwendungslaufzeit initialisiert) und Thread-sicher.

+11

Das Hinzufügen von 'faul' funktioniert, wenn Sie * wissen * Sie die Anweisungen in der falschen Reihenfolge ausführen. Aber wenn du denkst, dass du sie in der richtigen Reihenfolge hast, wirst du wahrscheinlich nicht daran denken, das "unnötige" "faule" hinzuzufügen. : / – kornfridge

2

Wie @paradigmatische Staaten, es ist nicht wirklich ein Fehler. Es ist die Initialisierungsreihenfolge, die der Deklarationsreihenfolge folgt. In diesem Fall ist a null, wenn b/init-ed deklariert ist.

Das Ändern der Zeile private val b = new B(a) zu private lazy val b = new B(a) wird das Problem beheben, da die Verwendung von faul die Init verzögern wird. von b zu seiner ersten Verwendung.

Es ist sehr wahrscheinlich, dass dieses Verhalten im SLS beschrieben wird.

7

Die Art, wie ich es verstehe, hat damit zu tun, wie die Scala-Klassen erstellt werden. In Java würde die oben definierte Klasse die Variablen inline initialisieren, und da a noch nicht definiert wurde, konnte sie nicht kompiliert werden. in Scala es jedoch, das entspricht dies in Java (die auch null im selben Szenario erzeugen sollte) ist:

class Omg { 
    private B b = null; 
    private A a = null; 

    Omg(){ 
    b = new B(a); 
    a = new A(); 
    } 
} 

Alternativ können Sie Ihre Erklärung b faul, machen, die den Wert Einstellung verschieben würde, bis es heißt (zu diesem Zeitpunkt wurde ein Wille gesetzt).

6

Wenn dies ein Problem ist, kompilieren Sie mit -Xcheckinit während der Entwicklung und iterieren, bis die Ausnahmen verschwinden.

Spec 5.1 für Schablonenkörperanweisungen, die in der Reihenfolge ausgeführt werden; Anfang 4.0 für Vorwärtsreferenzen in Blöcken.

Forward References - why does this code compile?