Scala用特征来实现混入(mix-in)式的多重继承

news/2024/7/7 19:20:33

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

Scala里相当于Java接口的是特征(Trait)。Trait的英文意思是特质和性状(本文称其为特征),实际上他比接口还功能强大。与接口不同的是,它还可以定义属性和方法的实现。Scala中特征被用于服务于单一目的功能模块的模块化中。通过混合这种特征(模块)群来实现各种应用程序的功能要求,Scala也是按照这个构想来设计的。 一般情况下Scala的类只能够继承单一父类,但是如果是特征的话就可以继承多个,从结果来看就是实现了多重继承。这可以被认为是同Ruby模块基本相同的功能。就看一下下面的例子吧。为了辨认方便,此后的特征名称前都加上前缀字母T。特征既可以继承类也可以继承其他特征。

class Person ; //实验用的空类,如果使用了上一次的Person类,则下面的  

//PianoplayingTeacher类就需要构造参数了  

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. ")} //定义虚方法的实现  

}  

如上所示,可以连着多个with语句来混合多个特征到一个类中。第一个被继承源用extends,第二个以后的就用with语句。正如大家所知道的,可以生成实例的是非抽象(abstract)的类。另外请注意一下从特征是不可以直接创建实例的。

那么就实际运行一下吧。

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  

}  

接着作为被加入的方面,定义一下加入了前置处理和后置处理的特征TBeforeAfter。

trait TBeforeAfter extends TAction {  

abstract override def doAction {  

println("/entry before-action") //doAction的前置处理  

super.doAction // 调用原来的处理  

println("/exit after-action") //doAction的后置处理  

}  

}  

通过上面的abstract override def doAction {}语句来覆盖虚方法。具体来说这当中的super.doAction是关键,他调用了TAction的doAction方法。其原理是,由于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 )  

}  

}  

}  

下面,将TBeforeAfter和TtwiceAction混合在一起后执行一下。

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 

伴随着原来方法的before/after动作一起各自执行了两次。接着将混入顺序颠倒后再试一下。

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是怎样的强类型和静态化语言的话,那么就能够明白通过特征来加入新功能的特

点给他带来了多大的灵活性。

转载于:https://my.oschina.net/ytqvip/blog/750215


http://www.niftyadmin.cn/n/599445.html

相关文章

spring 配置文件 数据库引入

一.mysql数据库引入 <bean id"dataSource" class"com.mchange.v2.c3p0.ComboPooledDataSource"> <property name"driverClass" value"com.mysql.jdbc.Driver" /> <property name"jdbcUrl" value"jdbc:m…

PHP IDE,Visual Studio还是Eclipse?

<iframe align"top" marginwidth"0" marginheight"0" src"http://www.zealware.com/46860.html" frameborder"0" width"468" scrolling"no" height"60"></iframe>每个程序员都有…

MSN中英双语聊天机器人上线,邀请测试

<iframe align"top" marginwidth"0" marginheight"0" src"http://www.zealware.com/46860.html" frameborder"0" width"468" scrolling"no" height"60"></iframe>我业余做的一个…

httpwebrequest 请求压缩,接受压缩的字符流请求

请看图&#xff0c;客户端和服务端都使用gzip压缩。 客户端参数以json字符串形式gzip压缩&#xff0c;以二进制流发送到服务端&#xff1b;服务端接收到请求&#xff0c;解压缩gzip后&#xff0c;进行业务逻辑处理&#xff1b;处理完后在将返回值以json形式&#xff0c;gzip压缩…

weblab需求

需求文档 weblab需求文档 web端、后端、idea端、部署端 web端主要功能用于信息管理和维护 web端需要分学生和教师端 认证授权 采用springboot和redis,签发token 配置独立的redis服务器用于存放用户session 目前redis服务器地址&#xff1a;123.56.220.39:6379 root 911ABCabc …

小大端

2019独角兽企业重金招聘Python工程师标准>>> intel x86是小端(高位存放在高地址&#xff0c;和我们逻辑是一样的)&#xff0c;网路字节是大端&#xff08;低位放在高地址&#xff09; bool isBigEndian() {unsigned int x 0x01020304; // 低字节0x04return *((unsi…

weblab-task

任务 文件服务器班级管理、邀请和加入 项目的创建和管理 建立项目 队伍 一开始 每个人单独 队长可以把这git地址发给他的队员 老师可以筛选只看队长 组织管理 组织表、用户组织表、组织通知作业表 增删改查 在一个组织里&#xff1a;学生列表&#xff0c;查看学生信息&…

华丽的Linux 3D桌面XGL(附精彩视频)

<iframe align"top" marginwidth"0" marginheight"0" src"http://www.zealware.com/46860.html" frameborder"0" width"468" scrolling"no" height"60"></iframe>Linux获得真正3…