Scala学习笔记--特质trait
http://outofmemory.cn/scala/scala-trait-introduce-and-example
与Java相似之处
Scala类型系统的基础部分是与Java非常相像的。Scala与Java一样有单一的根类,Java通过接口来实现多重继承,而Scala则通过特征(trait)来实现(Scala的特征可以包含实现代码,这当然是与Java接口不同的。不过由于特征自己具有类型的功能,所以对于没有包含实现代码的特征,可以认为与Java的接口是等价的)
用Trait来实现混入(mix-in)式的多重继承
Scala里相当于Java接口的是Trait(特征)。Trait的英文意思是特质和性状(本文称其为特征),实际上他比接口还功能强大。与接口不同的是,它还可以定义属性和方法的实现。Scala中特征被用于服务于单一目的功能模块的模块化中。通过混合这种特征(模块)群来实现各种应用程序的功能要求,Scala也是按照这个构想来设计的。
一般情况下Scala的类只能够继承单一父类,但是如果是特征的话就可以继承多个,从结果来看就是实现了多重继承。就看一下下面的例子吧。为了辨认方便,此后的特征名称前都加上前缀字母T。特征既可以继承类也可以继承其他特征。
特质的定义除了使用关键字trait以外,与类定义无异
我们再看下Flyable和Swimable两个 trait的实现:
trait Flyable { //特质1 def hasFeather = true def fly } trait Swimable { //特质2 def swim }
注意Flyable trait中有两个方法,一个是hasFeather方法,这个方法已经实现了,另一个方法是fly方法,这个方法只是定义没有实现,而Swimable trait只是定义个一个swim的方法,没有具体实现。
一旦特质被定义,就可以使用extends或with关键字,把它混入类中。Scala程序员“混入”特质而不是继承它们,因为“混入”与多重继承有明显差异
(1)用extends混入Flyable特质的类,
这种情况下隐式继承了特质的超类。就下面的例子来说,Bird类是AnyRef(AnyRef是Flyable的超类)的子类,并混入了Flyable特质。
class Bird extends Flyable{ def fly()=println("Bird can fly!") }
特质同样也是类型,可以这么用:
val flyStyle1 : Flyable = new Bird;
val flyStyle2 : Flyable = new Flyable; // 错误的用法
(2)如果想把特质混入显式扩展超类的类里,可以用extends指明待扩展的超类,用with混入特质
我们先定义一个Aminmal类:
abstract class Animal { def walk(speed:Int) //抽象方法 def breathe() = { println("animal breathes") } }
这里的抽象类Animal定义了walk方法,实现了breathe方法。
下面我们定义一种动物,它既会飞也会游泳,这种动物是鱼鹰 FishEagle,我们看下代码:
class FishEagle extends Animal with Flyable with Swimable { def walk(speed:Int) = println("fish eagle walk with speed " + speed) def swim() = println("fish eagle swim fast") def fly() = println("fish eagle fly fast") }
FishEagle类继承自Animal,extends Animal后面有两个with,with Flyable和with Swimable,表示也具备两种特征。
在类的实现中需要实现抽象类Animal的walk方法,也需要实现两个特征中定义的未实现方法。
特质类似于带有具体方法的Java接口,不过特质能做更多的事情。
例如,特质可以声明字段和维持状态值。实际上,你可以用特质的定义做任何用类定义能做的事,并且除了以下两点以外连语法都是一样的。
第一点,特质不能有任何“类”参数,即传递给类的主构造器的参数。
eg
class Point(x:Int,y:Int) //正确 trait NoPoint(x:Int,y:Int)//不能编译通过
第二点,不论在类的哪个地方,super调用都是静态绑定的,而在特质中,他们是动态绑定的。
如果在类中写下“super.toString”,你很明确哪个方法实现将被调用。
然而如果在特质中写了同样的东西,在你定义特质的时候super调用的方法实现尚未被定义。调用的实现将在每一次特质被混入到具体类的时候才被决定。
这种处理super的有趣行为是使得特质能以可堆叠的改变方式工作的关键