2012-08-29 15 views
18

Eventuali duplicati:
Scala: forward references - why does this code compile?Scala e riferimenti in avanti

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

} 

il codice seguente stampa "null". In java la costruzione simile non viene compilata a causa di un riferimento forward non valido. La domanda è: perché compilare bene in Scala? È questo per progettazione, descritto in SLS o semplicemente bug in 2.9.1?

+1

Il problema che mi infastidisce di questo è che permette un val di cambiare il suo valore. Questo mi rende triste :-( – thoredge

+0

è un po 'strano - molti errori potrebbero essere causati da questo, e mi sono basato sul comportamento di Java, che richiede l'inizializzazione dei valori prima che vengano utilizzati. – jdevelop

+1

@jdevelop Anche java non cattura tutto possibili riferimenti futuri –

risposta

23

Non è un errore, ma un errore classico durante l'apprendimento di Scala. Quando l'oggetto Omg viene inizializzato, tutti i valori vengono prima impostati sul valore predefinito (null in questo caso) e quindi viene eseguito il costruttore (ovvero il corpo dell'oggetto).

per farlo funzionare, basta aggiungere la parola chiave lazy di fronte alla dichiarazione che si sta in avanti fa riferimento (valore a in questo caso):

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

Valore a saranno poi inizializzati su richiesta.

Questa struttura è veloce (i valori vengono inizializzati solo una volta per tutti i runtime dell'applicazione) e thread-safe.

+11

L'aggiunta di 'pigro' funziona se * sai * che stai eseguendo le istruzioni nell'ordine sbagliato. Ma se pensi * di averli nell'ordine corretto, probabilmente non penserai di aggiungere quel "non necessario" pigro. : / – kornfridge

2

Come gli stati di @paradigmatic, non è proprio un bug. È l'ordine di inizializzazione, che segue l'ordine di dichiarazione. In questo caso, a è nullo quando b è dichiarato/inizializzato.

La modifica della riga private val b = new B(a) a private lazy val b = new B(a) risolverà il problema, poiché l'utilizzo di lazy ritarderà l'init. di b al suo primo utilizzo.

È molto probabile che questo comportamento sia descritto in SLS.

7

Il modo in cui l'ho capito, questo ha a che fare con il modo in cui vengono create le classi di Scala. In Java, la classe sopra definita avrebbe inizializzato le variabili in linea, e dal momento che a non era ancora stata definita, non poteva essere compilata. Tuttavia, in Scala è più l'equivalente di questo in Java (che dovrebbe anche produrre nulla nello stesso scenario):

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

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

In alternativa, si potrebbe rendere il vostro dichiarazione di b pigro, che rinviare l'impostazione del valore fino a quando è chiamato (in quel momento è stato impostato un testamento).

6

Se questo è un problema, compilare con -Xcheckinit durante lo sviluppo e iterare finché le eccezioni non scompaiono.

Spec 5.1 per le dichiarazioni del corpo modello eseguite nell'ordine; inizio di 4.0 per i riferimenti avanzati nei blocchi.

Forward References - why does this code compile?