1. 为什么没有多重继承
scala和java一样不允许类从多个超类继承。
但是scala提供“特质”而非接口。特质可以同时拥有抽象方法和具体方法,而类可以实现多个特质。
2. 当做接口使用的特质
scala特质完全可以像java接口那样工作。例如:
不需要将方法声明为abstract——特质中未被实现的方法默认为抽象方法trait Logger{ def log(msg:String)//抽象方法 }
子类可以给出实现:
如果需要多个特质,可以用with关键字来添加额外的特质:class ConsoleLogger extends Logger{//使用extends而非implements def log(msg:String){ println(msg)}//不需要写override }
class ConsoleLogger extends Logger with Cloneable with Serializable
所有java中的接口都可以作为scala特质使用。
Scala类只能继承一个超类,但可以有任意数量的特质
3. 带有具体实现的特质
scala中,特质中的方法并不需要一定是抽象的。例如:trait ConsoleLogger{
def log(msg:String){println(msg)
}
ConsoleLogger特质提供了一个带有实现的方法
4. 带有特质的对象
例如scala标准库中logged特质:trait Logged{
def log(msg:String){)
}
可以扩展logged特质:
trait ConsoleLogger extends Logged{
override def log(msg:String){println(msg)
}
可以在构造对象时,添加特质。可以在acct对象上调用log方法。
val acct=new SavingAccount with ConsoleLogger
也可以在另一个对象上加入不同特质。
val acct2=new SavingAccount with FileLogger
5. 叠加在一起的特质
可以为类或者对象添加多个互相调用的特质,从最后一个开始。这对于需要分阶段加工处理某个值的场景很有用。
例如:需要给所有日志消息添加时间戳:
trait TimestampLogger extends Logged{
override def log(msg:String){
super.log(new java.util.Date()+" "+ msg)
}
}
需要截断过长的日志消息:
例如:trait ShortLogger extends Logged{ override def log(msg:String){ super.log( if(msg.length<15) msg else msg.substring(0,15-3)+"... ") } }
val acct1=new SavingAccount with ConsoleLogger with TimestampLogger with ShortLogger
val acct2=new SavingAccount with ConsoleLogger with ShortLogger with TimestampLogger
acct1取款: Sun Feb 06 17:45:45 ICT 2017 Instufficient... //shortlogger先执行
acct2取款: Sun Feb 06 1... //timestamplogger先执行
6. 在特质中重写抽象方法
trait TimestampLogger extends Logger{
override def log(msg:String){
super.log(new java.util.Date()+" "+ msg)
}
}
编译器将super.log调用标记为错误
scala认为TimestampLogger依旧是抽象的,需要混入一个具体的log方法。因此正确方法:
trait TimestampLogger extends Logger{
abstract override def log(msg:String){
super.log(new java.util.Date()+" "+ msg)
}
}
7. 当做富接口使用的特质
特质可以包含大量工具方法,而这些工具方法可以依赖一些抽象方法来实现88. 特质中的具体字段
特质中的字段可以是具体的,也可以是抽象的。若给出初始值,则字段是具体的。对于特质中的每一个具体字段,使用该特质的类都会获得一个字段与之对应。这些字段不是被继承的;只是简单地被加到子类当中。9. 特质中的抽象字段
特质中未被初始化的字段在具体的子类中必须被重写。
10. 特质构造顺序
特质也可以有构造器,由字段的初始化和其他特质体中的语句构成。例如:
trait FileLogger extends Logger{
val out =new PrintWriter("app.log")// 特质构造器的一部分
out.println(new Date().toString) //特质构造器的一部分
def log(msg:String){ out.println(msg);out.flush()}
}
构造器以如下顺序执行:
- 首先调用超类的构造器
- 特质构造器在超类构造器之后、类构造器之前执行
- 特质由左到右被构造
- 每个特质当中,父特质先被构造
- 如果多个特质共有一个父特质,而那个父特质已经被构造,则不会被再次构造
- 所有特质构造完毕,子类被构造
11. 初始化特质中的字段
特质不能有构造参数。每个特质都有一个无参数的构造器。12. 扩展类的特质
特质可以扩展另一个特质,而特质组成的继承层级也很常见。特质也可以扩展类。这个类会自动成为所有混入该特质的超类。