(转自http://www.qqread.com/other-devtool/f484284.html)
与Java相似之处
Scala类型系统的基础部分是与Java非常相像的。Scala与Java一样有单一的根类,Java通过接口来实现多重继承,而Scala则通过特征(trait)来实现(Scala的特征可以包含实现代码,这当然是与Java接口不同的。不过由于特征自己具有类型的功能,所以对于没有包含实现代码的特征,可以认为与Java的接口是等价的)
用Trait来实现混入(mix-in)式的多重继承
Scala里相当于Java接口的是Trait(特征)。Trait的英文意思是特质和性状(本文称其为特征),实际上他比接口还功能强大。与接口不同的是,它还可以定义属性和方法的实现。Scala中特征被用于服务于单一目的功能模块的模块化中。通过混合这种特征(模块)群来实现各种应用程序的功能要求,Scala也是按照这个构想来设计的。
一般情况下Scala的类只能够继承单一父类,但是如果是特征的话就可以继承多个,从结果来看就是实现了多重继承。就看一下下面的例子吧。为了辨认方便,此后的特征名称前都加上前缀字母T。特征既可以继承类也可以继承其他特征。
- class Person ; //实验用的空类
- trait TTeacher extends Person {
- def teach //虚方法,没有实现
- }
- trait TPianoPlayer extends Person {
- def playPiano = {println("I’m playing piano. ")} //实方法,已实现
- }
- class PianoplayingTeacher extends Person with TTeacher with TPianoPlayer {
- def teach = {println("I’m teaching students. ")} //定义虚方法的实现
- }
那么就实际运行一下吧。
- scala> val t1 = new PianoplayingTeacher
- t1: PianoplayingTeacher = PianoplayingTeacher@170a650
- scala> t1.playPiano
- I’m playing piano.
- scala> t1.teach
- I’m teaching students.
- scala> val tanakaTaro = new Person with TTeacher with TPianoPlayer {
- | def teach = {println("I'm teaching students.")} }
- tanakaTaro: Person with TTeacher with TPianoPlayer = $anon$1@5bcd91
- scala> tanakaTaro playPiano
- I’m playing piano.
- scala> tanakaTaro teach
- I'm teaching students.
充分利用特征的功能之后,就能方便地实现现今流行的面向方面编程(AOP)了。
首先,用特征来声明表示基本动作方法的模块Taction。
- trait TAction {
- def doAction
- }
- trait TBeforeAfter extends TAction {
- abstract override def doAction {
- println("/entry before-action") //doAction的前置处理
- super.doAction // 调用原来的处理
- println("/exit after-action") //doAction的后置处理
- }
- }
那么将实际执行的实体类RealAction作为TAction的子类来实现吧。
- class RealAction extends TAction {
- def doAction = { println("** real action done!! **") }
- }
- scala> val act1 = new RealAction with TBeforeAfter
- act1: RealAction with TBeforeAfter = $anon$1@3bce70
- scala> act1.doAction
- /entry before-action
- ** real action done!! **
- /exit after-action
- trait TTwiceAction extends TAction {
- abstract override def doAction {
- for ( i <- 1 to 2 ) { // 循环执行源方法的方面
- super.doAction // 调用源方法doAction
- println( " ==> No." + i )
- }
- }
- }
- scala> val act2 = new RealAction with TBeforeAfter with TTwiceAction
- act2: RealAction with TBeforeAfter with TTwiceAction = $anon$1@1fcbac1
- scala> act2.doAction
- /entry before-action
- ** real action done!! **
- /exit after-action
- ==> No.1
- /entry before-action
- ** real action done!! **
- /exit after-action
- ==> No.2
- scala> val act3 = new RealAction with TTwiceAction with TBeforeAfter
- act3: RealAction with TTwiceAction with TBeforeAfter = $anon$1@6af790
- scala> act3.doAction
- /entry before-action
- ** real action done!! **
- ==> No.1
- ** real action done!! **
- ==> No.2
- /exit after-action
这样执行后,原来的实现方法被循环执行了两次,但是before/after则在循环以外整体只执行了一次。这是根据with语句定义的顺序来执行的,知道了这原理之后也就没有什么奇怪的了。Scala特性的如此混入顺序是和AspectJ的方面以及Spring的interceptor相同的。
这样不仅是before和after动作,只要更改了特征的实现就可以将各种方面动态地加入到原来的对象中去了,读者自己也可以尝试一下各种其他情况。
在Java中通过Decorator或Template Method模式来想尽办法实现的功能,在Scala中只要通过特征就可以轻松到手了。从这还可以延展开来,通过在原来的方法中插入挂钩的方法,即所谓的拦截者式面向方面的方法,就可以轻松地将各个方面通过特征来组件化了。
请读者如果想起Scala是怎样的强类型和静态化语言的话,那么就能够明白通过特征来加入新功能的特点给他带来了多大的灵活性。