2012-10-22 3 views
5

Ich habe ein etwas bizzar Verhalten in Objekten, die App erweitert. Werfen Sie einen Blick auf die folgende ERSATZ-Befehle:Was passiert in scala beim Laden von Objekten, die App erweitert?

scala> object A extends App {val x = "I am null"} 
defined module A 

scala> object B {val x = "I am a string"} 
defined module B 

scala> A.x 
res0: java.lang.String = null 

scala> B.x 
res1: java.lang.String = I am a string 

gut, das ist ein bisschen komisch ist ... aber es wird weirder. Ich dachte dann die vals in einem object in eine faule Auswertung gehen ... so habe ich versucht, eine echte lazy val:

scala> object C extends App {lazy val x = "What am I?"} 
defined module C 

scala> C.x 
res2: java.lang.String = What am I? 

so was hier passiert? Warum erhält ein reguläres val einen Nullwert?
Warum ändert sich dieses Verhalten, wenn ich lazy val verwende?
und was ist so besonders mit der Eigenschaft App, dass die regelmäßigen Vals nicht bewertet werden?

Antwort

9

App erweitert DelayedInit Merkmal. So werden alle Anweisungen und alle Wertdefinitionen zu delayedInit Methode verschoben. Lazy Val funktioniert, weil es nach Methode kompiliert wird.

Zum Beispiel, wenn Sie decompile diese Klasse:

class TestApp extends App{ 
    val test = "I am null" 
    lazy val testLazy ="I am a string" 
} 

Sie Klasse bekommen mit 'faulen Methode':

public String testLazy() 
{ 
    if((bitmap$0 & 1) == 0) 
     synchronized(this) 
     { 
      if((bitmap$0 & 1) == 0) 
      { 
       testLazy = "I am a string"; 
       bitmap$0 = bitmap$0 | 1; 
      } 
      BoxedUnit _tmp = BoxedUnit.UNIT; 
     } 
    return testLazy; 
} 

und delayedInit Methode in innere Klasse delayedInit.body:

public final class delayedInit.body extends AbstractFunction0 
     implements ScalaObject 
    { 

     public final Object apply() 
     { 
      $outer.test_$eq("I am null"); 
      return BoxedUnit.UNIT; 
     } 

     private final TestApp $outer; 
.... 

Der Wert "I am null" wird nur dann dem Feld test zugewiesen, wenn d elayedInit wird aufgerufen.