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实例