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的機會個人感覺都不多。
蘑菇雲代碼
package com.dt.scala.oop
/**
* Created by Administrator on 2016/8/19.
*/
object AppInternals extends App{
val c = new C
println("Hello Spark")
}
trait Helper extends DelayedInit {
def delayedInit(body: => Unit) = {
println("dummy text, printed before initialization of C")
body // evaluates the initialization code of C
}
}
class C extends Helper {
println("this is the initialization code of C")
}
----------
結果
dummy text, printed before initialization of C
this is the initialization code of C
Hello Spark
補充
其實上面講的並沒有說清楚,是怎麼把封裝的初始化代碼塊傳給delayedInit(body: => Unit)的。
用反編譯工具jd-gui.exe把上面生成的.class反編譯出來,可以看到多出了好多個類,
其中主要的有
class AppInternals, delayedInit
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 Helper
{
Predef..MODULE$.println(“dummy text, printed before initialization of C”);
body.apply
}
這就是初始化代碼塊的執行順序,在main方法裏調用delayedInit方法,先是執行
Predef..MODULE$.println(“dummy text, printed before initialization of C”);
之後調用 body.apply
body.apply
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
補充內容是自己猜測的居多,不保證正確,個人覺得背後的原理其實不用深究,知道有這麼回事就行了。
以上內容來自[DT大數據夢工廠]首席專家Spark專家王家林老師的課程分享。感謝王老師的分享,更多精彩內容請掃描關注[DT大數據夢工廠]微信公衆號DT_Spark