2012-08-29 14 views
18

Duplicar posibles:
Scala: forward references - why does this code compile?Scala y referencias hacia delante

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

} 

el siguiente código imprime "nulo". En java construcción similar no se compila debido a una referencia directa no válida. La pregunta es: ¿por qué compila bien en Scala? ¿Es eso por diseño, descrito en SLS o simplemente error en 2.9.1?

+1

La cuestión que me molesta de esto es que permite un val para cambiar su valor. Eso me pone triste :-( – thoredge

+0

es un poco extraño - muchos errores pueden ser causados ​​por eso, y me basé en el comportamiento de Java, que requiere tener los valores inicializados antes de ser usados. – jdevelop

+1

@jdevelop Incluso java no capta todo posibles referencias hacia adelante –

Respuesta

23

No es un error, sino un error clásico cuando se aprende Scala. Cuando se inicializa el objeto Omg, todos los valores se establecen primero en el valor predeterminado (null en este caso) y luego se ejecuta el constructor (es decir, el cuerpo del objeto).

para hacer que funcione, sólo tiene que añadir la palabra clave lazy delante de la declaración está haciendo referencia hacia adelante (valor a en este 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) 
    } 
} 

Valor a luego será inicializado bajo demanda.

Esta construcción es rápida (los valores solo se inicializan una vez para todo el tiempo de ejecución de la aplicación) y es segura para la ejecución de subprocesos.

+11

Agregar 'lazy' funciona si * sabes * estás ejecutando las instrucciones en el orden incorrecto. Pero si * piensas * que los tienes en el orden correcto, probablemente no pensarás en agregar ese 'innecesario' 'perezoso'. : / – kornfridge

2

Como @paradigmatic declara, no es realmente un error. Es el orden de inicialización, que sigue el orden de la declaración. En este caso, a es nulo cuando b se declara/inicia.

Cambiando la línea private val b = new B(a) a private lazy val b = new B(a) se solucionará el problema, ya que usar perezoso retrasará el inicio. de b a su primer uso.

Es muy probable que este comportamiento se describa en el SLS.

7

La forma en que lo entiendo, esto tiene que ver con cómo se crean las clases de Scala. En Java, la clase definida anteriormente estaría inicializando las variables en línea, y dado que no se había definido aún el a, no se pudo compilar. Sin embargo, en la Scala es más el equivalente de esto en Java (que también debe producir nulo en el mismo escenario):

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

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

Alternativamente, usted podría hacer su declaración de b perezoso, que diferiría establecer el valor hasta se llama (en ese momento se habrá establecido un).

6

Si esto es una preocupación, compile con -Xcheckinit durante el desarrollo e itere hasta que las excepciones desaparezcan.

Spec 5.1 para declaraciones de cuerpo de plantilla ejecutadas en orden; comienzo de 4.0 para referencias hacia adelante en bloques.

Forward References - why does this code compile?

Cuestiones relacionadas