Scala--特质
一、为什么没有多重继承
c++允许多重继承
Java不允许多重继承,类只能继承一个超类,可以实现任意数量的接口。
如何继承这两个抽象基类?
Scala提供“特质”而非接口;特质可以同时抽象方法和具体方法。类可以实现多个特质。
二、当做接口使用的特质
- trait Logger{
- def log(msg: String)
- }
- class ConsoleLogger extends Logger{ //使用 extends 不能使用 implements
- def log(msg: String): Unit ={ // 不需要使用override
- println(msg)
- }
- }
三、带有具体实现的特质
- trait Logger{
- def log(msg: String){println(msg)}
- }
- class SavingAccount extends Logger{
- def display(msg: String): Unit ={
- log(msg)
- }
- }
- val v = new SavingAccount()
- v.display("Hello")
注:特质发生变化,所有混入特质的类都需要重新编译。
四、带有特质的对象
- trait Logged{
- def log(msg: String){} //特质,方法是个空实现
- }
- trait ConsoleLogger extends Logged{
- override def log(msg: String) {println(msg)}
- }
- class SavingAccount extends Logged{ //类继承特质,方法的实现结果为空
- def display(msg:String){log(msg)
- }
- }
- val s = new SavingAccount() //构造对象,结果为空
- s.display("Hello")
- val s1 = new SavingAccount() with ConsoleLogger //在构造对象的时候,加入特质,结果为 Hello
- s1.display("Hello")
五、叠加在一起的特质
- trait Logged{
- def log(msg: String){}
- }
- trait ConsoleLogger extends Logged{
- override def log(msg: String) {println(msg)}
- }
- trait TimestampLogger extends Logged{
- override def log(msg: String){
- super.log(new java.util.Date() + " " + msg)
- }
- }
- trait ShortLogger extends Logged{
- val maxLength = 15
- override def log(msg: String){
- super.log(
- if(msg.length <= maxLength) msg else msg.substring(0, maxLength-3) + "..."
- )
- }
- }
- class SavingAccount extends Logged {
- def display(msg: String) {
- log(msg)
- }
- }
- val s = new SavingAccount with ConsoleLogger with TimestampLogger with ShortLogger //ShortLogger的log方法先执行,然后是 TimestampLogger
- s.display("Hello")
- val s1 = new SavingAccount with ConsoleLogger with ShortLogger with TimestampLogger //TimeStampLogger的方法先执行,然后是 ShortLogger
- s1.display("Hello")
结果:
- Wed Aug 31 23:27:53 CST 2016 Hello
- Wed Aug 31 2...
六、在特质中重写抽象方法
- trait Logged{
- def log(msg: String)
- }
- trait ConsoleLogger extends Logged{
- override def log(msg: String) {println(msg)}
- }
- trait TimestampLogger extends Logged{
- abstract override def log(msg: String){ //继承抽象方法,需要使用abstract关键字,对抽象方法进行重写
- super.log(new java.util.Date() + " " + msg)
- }
- }
- class SavingAccount extends ConsoleLogger {
- def display(msg: String) {
- log(msg)
- }
- }
- val s = new SavingAccount with TimestampLogger
- s.display("Hello")
结果:
- Wed Aug 31 23:51:40 CST 2016 Hello
七、当做富接口使用的特质
- trait Logged{ // 特质将抽象方法,和具体方法结合在一起
- def log(msg: String)
- def info(msg: String){log(msg)}
- def warn(msg: String){log(msg)}
- }
- trait ConsoleLogger extends Logged{
- override def log(msg: String) {println(msg)}
- }
- trait TimestampLogger extends Logged{
- abstract override def log(msg: String){ //继承抽象方法,需要使用abstract关键字,对抽象方法进行重写
- super.log(new java.util.Date() + " " + msg)
- }
- }
- class SavingAccount extends ConsoleLogger {
- def display(msg: String) {
- info(msg)
- }
- }
- val s = new SavingAccount with TimestampLogger
- s.display("Hello")
结果:
- Thu Sep 01 09:38:54 CST 2016 Hello
八、特质中的具体字段
在子类中添加特质,特质中的字段就相当于子类中的字段
九、特质中的抽象字段
特质中的抽象字段,在子类的具体实现中必须要被重写
- trait Logged{ // 特质将抽象方法,和具体方法结合在一起
- val maxLength: Int // 抽象字段
- def log(msg: String)
- def info(msg: String){log(msg)}
- def warn(msg: String){log(msg)}
- }
- trait ConsoleLogger extends Logged{
- override def log(msg: String) {println(msg)}
- }
- trait TimestampLogger extends Logged{
- abstract override def log(msg: String){ //继承抽象方法,需要使用abstract关键字,对抽象方法进行重写
- super.log(new java.util.Date() + " " + msg)
- }
- }
- class SavingAccount(val maxLength: Int) extends ConsoleLogger { //抽象字段当做参数传递
- //val maxLength = 20 //抽象字段在类中被重写
- def display(msg: String) {
- info(msg)
- info(maxLength.toString)
- }
- }
- val s = new SavingAccount(20) with TimestampLogger //传递参数
- s.display("Hello")
结果:
- Thu Sep 01 10:05:55 CST 2016 Hello
- Thu Sep 01 10:05:55 CST 2016 20
十、特质构造顺序
1、首先调用超类的构造器
2、然后调用特质构造器,特质构造器在超类构造器之后,类构造器之前
3、特质由左到右被构造
4、在每个特质当中,父特质先被构造
5、如果多个特质公用一个父特质,而那个父特质已经被构造过了,则不会再被构造
6、所有特质构造完毕,子类被构造
例子:
- class SavingAccount extends Account with FileLogger with ShortLogger
构造器执行顺序:
1、Account(超类)
2、Logger(第一个特质的父特质)
3、FileLogger(第一个特质)
4、ShortLogger(第二个特质)
5、SavingAccount(类)
特质方法Super被解析的顺序 从右向左
ShortLogger(super)->FileLogger(super)->Logger
- trait Logged{
- def log(msg: String){}
- }
- trait ConsoleLogger extends Logged{
- override def log(msg: String) {println(msg)}
- }
- trait TimestampLogger extends Logged{
- override def log(msg: String){
- println("This is TimestampLogger")
- super.log(new java.util.Date() + " " + msg)
- }
- }
- trait ShortLogger extends Logged{
- val maxLength = 15
- override def log(msg: String){
- println("This is ShortLogger")
- super.log(
- if(msg.length <= maxLength) msg else msg.substring(0, maxLength-3) + "..."
- )
- }
- }
- class SavingAccount extends Logged {
- def display(msg: String) {
- log(msg)
- }
- }
- val s = new SavingAccount with ConsoleLogger with TimestampLogger with ShortLogger
- s.display("Hello")
- val s1 = new SavingAccount with ConsoleLogger with ShortLogger with TimestampLogger
- s1.display("Hello")
结果:
- This is ShortLogger
- This is TimestampLogger
- Thu Sep 01 13:55:33 CST 2016 Hello
- This is TimestampLogger
- This is ShortLogger
- Thu Sep 01 1...
十一、初始化特质中的字段
特质中不能使用构造参数
要想初始化特质中的字段,可使用如下方式:
1、提前定义,在特质的构造函数之前定义
- trait Logger{
- def log(msg: String){}
- }
- trait FileLogger extends Logger{
- val filename: String
- val out = new PrintWriter(filename)
- override def log(msg: String) {
- out.println(msg)
- out.flush()
- }
- }
- class SavingAccount extends Logger{
- def display(msg: String): Unit ={
- log(msg)
- }
- }//val c = new SavingAccount with FileLogger{ val filename="mylog.txt" } //构造器运行在 FileLogger构造器之后,所以不会运行成功
- val c = new {val filename = "mylog.txt"} with SavingAccount with FileLogger //这里使用了提前定义,可以正确执行
- c.display("hello11")
2、使用lazy值
- trait Logger{
- def log(msg: String){}
- }
- trait FileLogger extends Logger{
- val filename: String
- lazy val out = new PrintWriter(filename) //使用懒值 out 在使用时,才会初始化,那时filename值已经被正确设置了
- override def log(msg: String) {
- out.println(msg)
- out.flush()
- }
- }
- class SavingAccount extends Logger{
- def display(msg: String): Unit ={
- log(msg)
- }
- }//val c = new SavingAccount with FileLogger{ val filename="mylog.txt" } //构造器运行在 FileLogger构造器之后,所以不会运行成功
- //val c = new {val filename = "mylog.txt"} with SavingAccount with FileLogger //这里使用了提前定义,可以正确执行
- val c = new SavingAccount with FileLogger { val filename="mylog.txt" }
- c.display("hello world")
懒值在每次使用前都会检查是否已经初始化,用起来并不是那么高效
3、使用类主构造器传参
- trait Logger{
- def log(msg: String){}
- }
- trait FileLogger extends Logger{
- val filename: String
- //lazy val out = new PrintWriter(filename) //使用懒值 out 在使用时,才会初始化,那时filename值已经被正确设置了
- val out = new PrintWriter(filename)
- override def log(msg: String) {
- out.println(msg)
- out.flush()
- }
- }
- // class SavingAccount extends Logger{
- // def display(msg: String): Unit ={
- // log(msg)
- // }
- // }
- class SavingAccount(val filename: String) extends Logger{
- def display(msg: String): Unit ={
- log(msg)
- }
- }
- //val c = new SavingAccount with FileLogger{ val filename="mylog.txt" } //构造器运行在 FileLogger构造器之后,所以不会运行成功
- //val c = new {val filename = "mylog.txt"} with SavingAccount with FileLogger //这里使用了提前定义,可以正确执行
- //val c = new SavingAccount with FileLogger { val filename="mylog.txt" }
- val c = new SavingAccount("mylog.txt") with FileLogger
- c.display("hello world 111")
十二、扩展类的特质
特质可以扩展另一个特质,特质也可以扩展类
类扩展了特质,特质扩展了类,特质的超类成为我们类的超类;
如果类扩展了另一个类,只要那个类是特质超类的子类就可以。
如果扩展多个类,且是不相干的,类不能有多个超类
- trait Logged{
- def log(msg: String){}
- }
- trait LoggedException extends Exception with Logged{ //特质扩展超类
- def log() {log(getMessage())}
- }
- class UnhappyException extends LoggedException{ //类扩展特质,特质的超类Exception 也成为了类UnHappyException的超类
- override def getMessage() = "aaa"
- }
- class UnhappyException extends IOException with LoggedException //可以扩展 IOException是 Exception的子类
- class UnhappyException extends JFrame with LoggedException // JFrame 和 Exception 没继承关系 不能扩展
十三、自身类型
this : Exception =>
- trait Logged{
- def log(msg: String){}
- }
- trait LoggedException extends Logged{ //特质扩展超类
- this: Exception => //自身类型 自身类型为Exception,它只能被混入Exception的子类
- def log() {log(getMessage())}
- }
- val f = new JFrame with LoggedException //错误 JFrame不是 Exception的子类型,而Exception是LoggedException的自身类型
结构类型:
- trait Logged{
- def log(msg: String){}
- }
- trait LoggedException extends Logged{ //特质扩展超类
- this: {def getMessage():String} => //自身类型(结构类型) 这个特质可以被混入任何拥有getMessage方法的类
- def log() {log(getMessage())}
- }
十四、背后发生了什么
参考《快学Scala》
Scala--特质的更多相关文章
- scala特质
package com.ming.test /** * scala 特质,类似与java接口,但是比java接口强大,可以有实现方法,定义字段之类的 */ /** * 定义一个日志的特质 */ tra ...
- Scala 特质全面解析
要点如下: Scala中类只能继承一个超类, 可以扩展任意数量的特质 特质可以要求实现它们的类具备特定的字段, 方法和超类 与Java接口不同, Scala特质可以提供方法和字段的实现 当将多个特质叠 ...
- scala akka 修炼之路5(scala特质应用场景分析)
scala中特质定义:包括一些字段,行为(方法/函数/动作)和一些未实现的功能接口的集合,能够方便的实现扩展或混入到已有类或抽象类中. scala中特质(trait)是一个非常实用的特性,在程序设计中 ...
- 8.scala:特质
版权申明:转载请注明出处.文章来源:http://bigdataer.net/?p=317 总体来说,scala中的特质类似于Java中的接口,但是有别于接口的是特质中既可以有实现方法也可以有抽象方法 ...
- scala 特质的应用
一.为类提供可以堆叠的改变 package com.jason.qianfeng trait Loggertest { def logger(msg: String) } trait ConsoleL ...
- scala学习笔记——特质
一个类扩展自一个或多个特质,以便使用这些特质提供的服务.特质可能会要求使用它的类支持某个特定的特性.不过和java不同,Scala特质可以给出这些特性的缺省实现. 特质的特性: 类可以实现任意数量的特 ...
- 快学Scala之特质
一个Scala类可以继承多个特质(trait), 特质可能会要求使用它们的类支持某个特定特性, 与Java接口不同, Scala特质可以给出这些特质的缺省实现. 要点如下: Scala中类只能继承一个 ...
- Scala入门2(特质与叠加在一起的特质)
一.介绍 参考http://luchunli.blog.51cto.com/2368057/1705025 我们知道,如果几个类有某些共通的方法或者字段,那么从它们多重继承时,就会出现麻烦.所以Jav ...
- 快学Scala习题解答—第十章 特质
10 特质 10.1 java.awt.Rectangle类有两个非常实用的方法translate和grow,但可惜的是像java.awt.geom.Ellipse2D这种类没有. 在Scala中,你 ...
- Scala学习十——特质
一.本章要点 类可以实现任意数量的特质 特质可以要求实现它们的类具备特定的字段,方法或超类 和Java接口不同,Scala特质可以提供方法和字段实现 当你将多个特质叠加在一起时,顺序很重要——其方法先 ...
随机推荐
- JS 显示隐藏DIV
JS关闭DIV HTML <div id="bar1"> <p onclick="removeElement('bar1')">关闭&l ...
- 安装php扩展包
sudo apt-get install php5-gd curl libcurl3 libcurl3-dev php5-curl 重启Apache sudo service apache2 rest ...
- Java内部类的介绍
在Java的面向对象编程中,由于Java并没有类似C++的多重继承,所以采用了内部类这样的方式,现在介绍几种内部类的常见情况. 公开内部类 即由public关键词修饰的内部类,内部类作为外部类的一个成 ...
- win7下解决vs2015新建项目,提示“未将对象引用设置到引用实例“的问题
问题描述: 打开vs2015新建c++项目时,出现有如下内容的对话框“未将对象引用设置到引用实例”的提示 解决方法: 1. 温馨提示:千万不要一冲动,就去卸载vs2015!! win7下安装vs20 ...
- C#版Aliyun DNS API
阿里云解析API,是为域名开发者.注册商.域名代理商等提供的开放和便捷的解析服务接口.API依托于万网云解析服务,可以方便的管理域名和解析记录,让你的解析管理变的随心省时自由舒畅. 一.先附上Aliy ...
- 【CLR Via C#】15 枚举类型与位类型
1.基础 枚举类型(enumerated types)定义了一组“符号名称/值”配对. 枚举类型是值类型,每个枚举类型都是从System.Enum派生的,而System.Enum又是从System.V ...
- OpenCV 图像特效
1.RGB ->灰度 #灰度 方式1 img=cv2.imread('b.png',0) img1=cv2.imread('b.png',1) height=img1.shape[0] widt ...
- JAVA JComboBox的监听事件(ActionListener、ItemListener)
版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] 参考资料: http://263229365.iteye.com/blog/1040329 https://www.ja ...
- Linux yum源详解
软件包安装方式 0.默认从官网下载包(国内,雅虎,网易,阿里云) cat /etc/yum.repos.d/rhel-source.repo [yum文件目录--redhat6版] [ ...
- Skype 服务器客户端策略参数优化
1.skype通讯录原理 对于skype客户端的通讯录同步,首先说说原理,通讯簿信息是从AD同步的skype前端服务器(每天1:30),在从前端服务器同步的客户端(大概1小时内同步一次). skype ...