App特質的作用
App特質的作用那就是延遲初始化,從代碼上看它繼承自DelayedInit,里面有個delayedInit方法 trait App extends DelayedInit DelayedInit特質里定義了延遲初始化方法: def delayedInit(x: => Unit): Unit 開發者可以直接在初始化塊里寫邏輯,(這里指的是 extends App{//todo}里的//todo代碼) 然后編譯器會把這段初始化代碼塊里的邏輯封裝成一個函數對象(是(() => Unit)類型) override def delayedInit(body: => Unit) { initCode += (() => body) } 緩存起來(並沒有運行),然后放到一個集合(ListBuffer)中,之后在main方法里一行一行調用並執行, 所以只有在執行到main方法的時候才會觸發,從而達到延遲初始化的效果。 def main(args: Array[String]) = { ... for (proc <- initCode) proc() ... }
不過在實際開發中,經常需要一開始就初始化,不然會報錯,如空指針異常,真正使用App的機會個人感覺都不多
object AppInternals extends App{ def testApp{ val c =new C println("3. Hello spark") } } trait Helper extends DelayedInit{ def delayedInit(body: => Unit)={ println("1. dummy text, printed before inititalization of C") body //evaluates the initialization code of C } } class C extends Helper{ println("2. this is the initialization code of C") } object AppTest { def main(args: Array[String]) { AppInternals.testApp } }
運行結果:
1. dummy text, printed before inititalization of C 2. this is the initialization code of C 3. Hello spark
問題: 是怎么把封裝的初始化代碼塊傳給delayedInit(body: => Unit)的?
用反編譯工具jd-gui.exe把上面生成的.class反編譯出來,可以看到多出了好多個類,
其中主要的有
class AppInternals, delayedInitbody,AppInternals, C, delayedInitbody,Helper,Helperclass
delayedInit$body出現兩次,一次出現在AppInternals中,一次出現在C中, 它里面都有一個方法
apply()
分別對應的是
public final Object apply()//AppInternals { this.$outer.c_$eq(new C()); Predef..MODULE$.println("Hello Spark"); return BoxedUnit.UNIT; } public final Object apply() { //C Predef..MODULE$.println("this is the initialization code of C"); return BoxedUnit.UNIT; }
從第一個apply中可以看出它已經把例子代碼中的代碼塊封裝起來了
object AppInternals extends App{ val c = new C println(“Hello Spark”) }
從第二個apply可以看出它把C中的
class C extends Helper { println(“this is the initialization code of C”) }
代碼塊封裝起來了
最后在class Helperclass里面用publicstaticvoiddelayedInit(Helperthis, Function0 body)
{ Predef..MODULE$.println(“dummy text, printed before initialization of C”); body.applymcVsp(); }
這就是初始化代碼塊的執行順序,在main方法里調用delayedInit方法,先是執行
Predef..MODULE$.println(“dummy text, printed before initialization of C”);
之后調用 body.applymcVsp(); body.applymcVsp()里先調用 this.outer.c_$eq(new C()); Predef..MODULE$.println(“Hello Spark”);
所以是先執行 this.outer.c_$eq(new C());
new C執行時就執行語句
println(“this is the initialization code of C”)
接着執行
println(“Hello Spark”);
所以輸出的結果就是這樣的
dummy text, printed before initialization of C this is the initialization code of C Hello Spark
補充內容是自己猜測的居多,不保證正確,個人覺得背后的原理其實不用深究,知道有這么回事就行了。