- 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
补充内容是自己猜测的居多,不保证正确,个人觉得背后的原理其实不用深究,知道有这么回事就行了。
来源: