文章出處

trait -- 不僅僅只是接口!

上回繼續,scala是一個非常有想法的語言,從接口的設計上就可以發現它的與眾不同。scala中與java的接口最接近的概念是trait,見下面的代碼:

package yjmyzz


object App {

  def main(args: Array[String]) {
    val bird = Bird("pigeon")
    bird.fly
    println(bird.isInstanceOf[Bird]) //true
    println(bird.isInstanceOf[Flyable]) //true
    println(bird.toString) //this is a Bird:pigeon
    bird.test //hello
  }
}

/**
 * 定義一個"接口"
 */
trait Flyable {

  /**
   * 定義接口方法
   */
  def fly;


  /**
   * 接口中也有可以方法的實現(是不是驚呆了!)
   * @return
   */
  def test = {
    println("hello")
  }
}


class Bird(var name: String) extends Flyable {
  /**
   * 實現接口中的方法
   */
  def fly: Unit = {
    println("I am a " + name + ", and I can fly~")
  }

  override def toString = {
    "this is a Bird:" + name
  }
}

object Bird {
  def apply(name: String) = {
    new Bird(name)
  }

}

從上面的代碼中,可以看出trait與java中interface的異同,相同的是如果把trait單純當接口來用,trait中只需要定義方法簽名即可,然后由"子類"來實現。不同的是,scala中的trait里也可以有方法實現!而且實現接口時,關鍵字不是implements而是extends(當然,還可能是with,后面還會提到),這說明scala中trait并不僅僅只是接口,它也是一種特殊的類

trait的mixin:

trait還有一個神奇的特性,可以在運行時,動態與其它類合體!見下面的代碼:

package yjmyzz


object App {

  def main(args: Array[String]) {
    val duck = new Bird("duck") with Swim
    println(duck.isInstanceOf[Flyable]) //true
    println(duck.isInstanceOf[Swim]) //true 注意這里:表示此時Bird也是一個Swim實例
    duck.fly //I am a duck, and I can fly~
    duck.swim //I can swim! 注:是不是很有意思,絕對的動態晚綁定!
  }
}

/**
 * 定義一個"接口"
 */
trait Flyable {

  /**
   * 定義接口方法
   */
  def fly;


  /**
   * 接口中也有可以方法的實現(是不是驚呆了!)
   * @return
   */
  def test = {
    println("hello")
  }
}


/**
 * 再來一個"接口"
 */
trait Swim {
  def swim = {
    println("I can swim!")
  }
}


class Bird(var name: String) extends Flyable {

  /**
   * 實現接口中的方法
   */
  def fly: Unit = {
    println("I am a " + name + ", and I can fly~")
  }

  override def toString = {
    "this is a Bird:" + name
  }
}

我們又新增了一個trait:Swim,然后注意第7行,通過with Swim,硬是把跟Swim毫不相關的Bird實例跟它攪在一起了,然后實例duck就獲得了Swim的能力! 這種場景下,trait就不應該理解為接口,而應該認為它是一種特性,是一種可以動態賦予其它實例的超能力!(這也是為什么關鍵字不叫interface,而是叫trait的原因吧)

trait與java中的接口還有一個明顯的區別,trait可以繼承自類,java中的interface可是不允許繼承自class的! 見下面的代碼示例:

package yjmyzz

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

/**
 * 會飛的動物("接口"繼承類)
 */
trait FlyAnimal extends Animal {
  def fly;
}

/**
 * 即會飛又會游泳的動物("接口"繼承"接口")
 */
trait SwimAndFlyAnimal extends FlyAnimal {
  def swim;
}

/**
 * 會說話(注:這個接口是完全獨立的,不繼承自任何其它類或trait)
 */
trait Talk {
  def talk;
}

/**
 * 鳥(繼承類,又實現"接口",實際上是多重繼承)
 */
class Bird(var birdName: String) extends Animal with FlyAnimal {
  override def fly: Unit = {
    println(birdName + "能飛!")
  }
}

/**
 * 被繼承的class[Animal]與trait[Talk]相互之間也可以沒半毛錢關系
 */
class AlienAnimal extends Animal with Talk {
  override def talk: Unit = println("外星動物很厲害的啦,它們會說話!")
}

/**
 * 類也可以直接繼承自trait
 */
class TalkThing extends Talk {
  override def talk: Unit = println("我也不知道我是啥,反正我會說話!")
}

object ScalaApp {

  def main(args: Array[String]) {

    var eagle = new Bird("老鷹")
    eagle.fly

    println

    var swan = new Bird("天鵝") with SwimAndFlyAnimal {
      override def swim: Unit = println("天鵝還能游泳!")
    }
    swan.fly
    swan.swim

    println

    var sparrow = new Bird("麻雀") with Talk {
      override def talk: Unit = println {
        "麻雀'說話',就是嘰嘰喳喳的叫!"
      }
    }
    sparrow.fly
    sparrow.talk

    println

    var alienAnimal = new AlienAnimal
    alienAnimal.talk

    println

    var talkThing = new TalkThing
    talkThing.talk


  }

}

運行結果:

老鷹能飛!

天鵝能飛!
天鵝還能游泳!

麻雀能飛!
麻雀'說話',就是嘰嘰喳喳的叫!

外星動物很厲害的啦,它們會說話!

我也不知道我是啥,反正我會說話!


關于trait,小結一下:

1、trait"類似"(注:僅僅只是類似)java中的接口,可以只定義方法簽名,交由子類去實現

2、trait中也可以有具體的方法實現

3、trait可以繼承自trait,也可以繼承自class

4、class也可以直接繼承自trait

5、trailt可以在運行時,通過with關鍵,動態混入class實例


文章列表




Avast logo

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


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

    IT工程師數位筆記本

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