文章出處

一、多繼承

上篇trait中,已經看到了其用法十分靈活,可以借此實現類似"多重繼承"的效果,語法格式為:

class/trait A extends B with C with D ...

之所以要給多重繼承加一個引號,是因為這有約束條件的,上面的語法中,從左向右看,extends 后的B是A的基本類型,不管后面接多少個trait,如果C或D,本身又繼承自其它class(上一篇講過,trait也可以繼承自class),則C或D的基類必須與B的基類保持一致,否則的話,JVM上的OOP世界觀將被徹底顛覆,scala編譯出來的class,也就沒辦法與java兼容了,這個原則,我個人叫做『同宗同源』,很容易理解,必須認同共同的祖先!當然,如果C或D,本身只是純粹的trait,不繼承自其它任何類,這就相當于一個A繼承自B,同時實現了多個接口,跟java中的理解一致。

package yjmyzz

/**
 * 動物基類
 */
class Animal {}

trait Fly {
  println("4 -> Fly")

  def fly
}

trait Swim {
  def swim
}

class FlyAnimal extends Animal with Fly {
  override def fly: Unit = println("I believe I can fly. I believe I can touch the sky")
}

trait FlyAndSwim extends Fly with Swim {

}


/**
 * 神基類
 */
class God {
  println("1 -> God")
}

/* with關鍵字只能用于trait,而不能是class
class HalfGod extends God with Animal{
  //Error:(14, 32) class Animal needs to be a trait to be mixed in
  //class HalfGod extends God with Animal{}
}
*/

trait Magic extends Animal {
  def showMagic
}

/* 無效繼承,因為HalfGod的基類為God,而magic的基類為Animal,它倆不是同一祖宗!
class HalfGod extends God with magic {
  //Error:(45, 32) illegal inheritance; superclass God
  //  is not a subclass of the superclass Animal
  //  of the mixin trait magic

  override def showMagic: Unit = println("I have some magic!")
}
*/

trait SuperPower {
  println("2 -> SuperPower")

  def superPower;
}

/**
 * "有超能力的"神
 */
class SuperPowerGod extends God with SuperPower {
  println("3 -> SuperPowerGod")

  override def superPower: Unit = println("I have super power!")
}

/**
 * "會飛的"神
 */
trait FlyGod extends God with Fly {
  println("5 -> FlyGod")

  override def fly: Unit = println("I can fly!")
}


/**
 * 多繼承示例(SuperPowerGod與FlyGod都是God的子類,因此類型兼容,編譯通過)
 */
class MyGod extends SuperPowerGod with FlyGod {
  println("6 -> MyGod")

}


object TestApp {

  def main(args: Array[String]) {
    var obj = new MyGod
    /*
    1 -> God
    2 -> SuperPower
    3 -> SuperPowerGod
    4 -> Fly
    5 -> FlyGod
    6 -> MyGod
    */
  }
}

代碼略長,但是并不難理解。比較有意思的是構造函數的調用順序,從輸出結果看,大致遵循下面的原則:

1、先調用父類的構造器(即:extends B中B的構造器,如果B還有父類,則先向上找,直到找到最高層的父類,然后調用頂級父類的構造器)

2、然后再調用With后的Trait的構造器,

  a)如果Trailt本身繼承自其它Class,則看下這個Class是不是步驟1中的父類,如果是的,就不重復調用了,最后輸出的4 -> Fly 前,并沒有重復輸出1 -> God 就說明了這一點

  b) 調用Trait本身的構造器

3、上述過程反復處理,只到把所有層級的基類處理完

4、最后再調用本身的構造器

 

二、AOP

談AOP之前,先來看看Scala的晚綁定:

package yjmyzz

trait IA {
  def foo = println("IA.foo()")
}

trait IAA extends IA{
  override def foo = println("IAA.foo()")
}

class A extends IA{
  override def foo = println("A.foo()")
}

object TestApp {

  def main(args: Array[String]) {

    val a = new A with IAA
    a.foo
    a.asInstanceOf[A].foo
    a.asInstanceOf[IA].foo
    a.asInstanceOf[IAA].foo
  }
}

最后的輸出是:

IAA.foo()
IAA.foo()
IAA.foo()
IAA.foo()

即:不管實例a轉型為什么類型,最終調用foo時,都是最底層的子類IAA里的foo方法,這就是晚綁定的特點。運行時,最底層的子類IAA已經override了父類的foo方法,所以最終不管怎么折騰,都是IAA里的override版本。

借助這個,就可以很方便的實現AOP,假設我們有一個業務處理類,想在業務處理前后,記錄日志,這是典型的AOP方法攔截場景,看下面的示例代碼:

package yjmyzz

/**
 * 業務接口
 */
trait Handler {
  def handle;
}

/**
 * 日志AOP
 */
trait LoggerHandler extends Handler {
  //注意這里的abstract不可省略,
  //因為super.Handle并沒有提供具體實現,而是在運行時,交由具體的子類來實現
  abstract override def handle = {
    println("log before handle...")
    super.handle
    println("log after handle...")
  }
}

/**
 * 業務處理類
 */
class BizHandler extends Handler {
  override def handle: Unit = println("business processing...")
}

object AopTest {

  def main(args: Array[String]) {
    var biz = new BizHandler with LoggerHandler;
    biz.handle //這里實際上調用的是LoggerHandler.handle

    //BizHandler為LoggerHandler的父類,所以運行時,
    // LoggerHandler.handle中的super.Handle才是真正調用的BizHandler.handle方法
  }


}

輸出結果:
log before handle...
business processing...
log after handle...

沒有反射,沒有動態代理,沒有借助第3方類庫,這是我見過的最簡潔的AOP實現。


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()