标签:
Scala中的特质与Java中的接口是比较类似的,但是Scala中的特质可以同时拥有抽象方法和具体方法,而类可以实现多个特质。下面我们详细讲解Scala中的特质这个强大的功能。
1. 把特质当作接口使用
我们定义一个trait,如下所示:
1 trait Logger { 2 def log(msg: String) 3 }
需要注意的是trait中未被实现的方法默认是抽象方法,因此不需要在方法前加abstract。
子类ConsoleLogger对Logger的实现,如下所示:
1 class ConsoleLogger extends Logger { 2 def log(msg: String): Unit = { 3 println(msg) 4 } 5 }
需要注意两点,一是实现时用的extends而不是implements;二是不需要在方法前写override关键字。
当我们需要的特质不止一个的时候,可以使用with关键字来添加其它的特质,如下所示:
1 class ConsoleLogger extends Logger with Cloneable with Serializable
我们使用Java类库中的Cloneable和Serializable接口作为特质,需要说明的是所有Java接口都可以作为Scala特质使用。
2. 带有具体实现的特质
带有具体方法的ConsoleLogger特质,如下所示:
1 trait Logger { 2 def log(msg: String): Unit = { 3 println(msg) 4 } 5 }
现在我们调用ConsoleLogger特质中的log(),如下所示:
1 class SavingsAccount extends Account with ConsoleLogger { 2 def withdraw (amount: Double): Unit = { 3 if (amount > balance) 4 log("Insufficient funds") 5 else 6 balance -= amount 7 } 8 ... 9 }
本质来说,就是SavingsAccount类混入了ConsoleLogger特质的功能,但是这样也存在一个不好的地方,那就是当特质发生改变时,所有混入了该特质的类都必须重新编译。
3. 带有特质的对象
我们定义Logged特质,如下所示:
1 trait Logged { 2 def log(msg: String): Unit = { 3 } 4 }
虽然Logged特质带了一个具体方法log(),但是该方法什么都不做。
我们在类SavingsAccount中使用Logged特质,如下所示:
1 class SavingAccount extends Account with Logged { 2 def withdraw (amount: Double): Unit = { 3 if (amount > balance) 4 log("Insufficient funds") 5 else 6 balance -= amount 7 } 8 ... 9 }
貌似上面的代码毫无意义,但是我们可以在构造具体对象的时候混入一个更好的实现,如下所示:
1 trait ConsoleLogger extends Logged { 2 override def log(msg: String): Unit = { 3 println(msg) 4 } 5 }
我们在构造对象acct的时候加入ConsoleLogger特质,如下所示:
1 val acct = new SavingsAccount with ConsoleLogger
4. 叠加在一起的特质
我们可以为类或对象添加多个互相调用的特质,并且调用从最后一个特质执行。这有什么用处呢?主要用于需要分阶段加工处理某个值的场景。
假设我们想给所有的日志消息添加时间戳,如下所示:
1 val acct = new SavingsAccount with ConsoleLogger 2 3 trait TimestampLogger extends Logged { 4 override def log(msg: String): Unit = { 5 super.log(new java.util.Data() + " " + msg) 6 } 7 }
同样地,假设我们想要截断过于冗长的日志消息,如下所示:
1 trait ShortLogger extends Logged { 2 val maxLength = 15 3 override deg log (msg: String) { 4 super.log ( 5 if (msg.length <= maxLength) msg else msg.substring(0, maxLength - 3) + 6 "...") 7 } 8 }
特别注意的是,super.log并不像类那样拥有相同的含义,当然如果含义相同,那么这些特质毫无用处,因为这些特质扩展的Logged特质中的log()什么也不做。实际上,super.log调用的是特质层级中的下一个特质,具体是哪一个,要根据特质添加的顺序来决定。我们会通过举例子来加以说明顺序的作用,如下所示:
1 val acct1 = new SavingsAccount with ConsoleLogger with TimestampLogger with ShortLogger 2 val acct2 = new SavingsAccount with ConsoleLogger with ShortLogger with TimestampLogger
如果执行第一条语句,我们会得到消息Sun Feb 06 17:45:45 ICT 2011 Insufficient...这说明ShortLogger中的log()首先被执行,然后它的super.log调用的是TimestampLogger。
如果执行第二条语句,我们会得到消息Sun Feb 06 1...这说明TimestampLogger中的log()首先被执行,然后它的super.log调用的是ShortLogger,其结果在之后被截断。
5. 在特质中重写抽象方法
6. 当做富接口使用的特质
7. 特质中的具体字段
8. 特质中的抽象字段
9. 特质构造顺序
10. 初始化特质中的字段
11. 扩展类的特质
参考文献:
[1] 快速了解Scala技术栈:http://www.infoq.com/cn/articles/scala-technology/
[2] 《快学Scala》
[3] 快学Scala第十章特质课后习题解答:http://css.gxzj.com.cn/News.aspx?id=383960
标签:
原文地址:http://www.cnblogs.com/shengshengwang/p/4853985.html