Scala進階之App特質

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, delayedInitbody,AppInternals , C, delayedInitbody,Helper,Helper class

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 HelperclasspublicstaticvoiddelayedInit(Helper this, Function0 body)
{
Predef..MODULE$.println(“dummy text, printed before initialization of C”);

body.applymcV sp();
}
這就是初始化代碼塊的執行順序,在main方法裏調用delayedInit方法,先是執行
Predef..MODULE$.println(“dummy text, printed before initialization of C”);

之後調用 body.applymcV sp();

body.applymcV sp()裏先調用

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

發佈了45 篇原創文章 · 獲贊 5 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章