一、为什么没有多重继承
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》