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實例
文章列表
留言列表