trait Logger {
def log(msg:String) //抽象方法
}
class ConsoleLogger extends Logger with Cloneable with Serializable{
def log(msg: String): Unit = {println(msg)}
}
注:1. 在重写特质的抽象方法时不需要给出override关键字;
2. 如果需要的特质不止一个,可以使用with关键字来添加额外的特质
trait Logger {
def log(msg:String) //抽象方法
}
trait ConsoleLogger{
def log(msg: String): Unit = {println(msg)}
}
class SavingAccounts extends Logger with ConsoleLogger{
var balance = 0: Double
def withdraw(amount: Double): Unit = {
if(amount > balance) log("Insufficient funds")
else balance -= amount
}
}
在这个例子中,SavingsAccount从ConsoleLogger特质得到了一个具体的log方法实现。用JAVA接口的话,这是不可能的。我们说ConsoleLogger的功能被“混入”了SavingsAccount类。
注:但是让特质拥有具体行为存在一个弊端。当特质改变时,所有混入了该特质的类都必须重新编译。
可以在构建单个对象时添加特质,表示在构造对象的时候“混入”了更好的类,在这个时候优先执行构建对象时添加的特质内的方法。
可以为类或对象添加多个相互调用的特质,从最后一个开始。
trait Logger {
def log(msg:String) { }
}
trait ConsoleLogger extends Logger{
override def log(msg: String): Unit = {println(msg)}
}
class SavingAccounts extends Logger with ConsoleLogger{
var balance = 0: Double
def withdraw(amount: Double): Unit = {
if(amount > balance) log("Insufficient funds")
else balance -= amount
}
}
trait TimestampLogger extends Logger{
override def log(msg: String): Unit = {
super.log(new java.util.Date() + " " + msg)
}
}
trait ShortLogger extends Logger{
val maxLength = 15
override def log(msg: String): Unit = {
super.log(if(msg.length <= maxLength)msg else msg.substring(0, maxLength-3) + "...")
}
}
object TestTrait {
def main(args: Array[String]): Unit = {
val acct1 = new SavingAccounts with ConsoleLogger with TimestampLogger with ShortLogger
val acct2 = new SavingAccounts with ConsoleLogger with ShortLogger with TimestampLogger
acct1.withdraw(1.0)
acct2.withdraw(1.0)
}
}
执行TestTrait对象的main方法结果如下:

acct1首先执行ShortLogger的log方法,然后用super.log调用TimestampLogger
acct2正好相反
如果需要控制具体是哪一个特质的方法被调用,则可以在方括号中给出名称:super[ConsoleLogger].log(...)。这里给出的类型必须是直接超类型;你无法使用继承层级中更远的特质或类。
trait Logger {
def log(msg:String)
def info(msg: String) {log("INFO: " + msg)}
def warn(msg: String) {log("WARN: " + msg)}
def severe(msg: String) {log("SEVERE: " + msg)}
}
class SavingAccounts extends Logger{
var balance = 0: Double
def withdraw(amount: Double): Unit = {
if(amount > balance) severe("Insufficient funds")
else balance -= amount
}
override def log(msg: String): Unit = {println(msg)}
}
特质中的字段可以是具体的,也可以是抽象的。如果给出了初始值,那么字段就是具体的。
来自特质的字段被加入子类字段。
特质中未被初始化的字段在具体的子类中必须被重写
构造器将按照如下的顺序执行:
举例来说,考虑如下一个类:
class SavingsAccount extends Account with FileLogger with ShortLogger
构造器将按照如下的顺序构造
特质不能有构造器参数,这是特质与类的唯一技术差别
在特质中放置一个抽象字段,在子类的构造函数中对这个抽象字段进行初始化是不可行的:
trait FileLogger extends Logger{
val filename: String
val out = new PrintStream(filename)
override def log(msg: String): Unit = {out.println(msg); out.flush()}
}
object TestTrait {
def main(args: Array[String]): Unit = {
val acct = new SavingAccounts with FileLogger {
override val filename: String = "myapp.log"
}
}
}
在这种情况下,由于FileLogger优先于子类被构造(子类就是一个扩展自SavingAccounts,混入FileLogger的匿名类),故在对FileLogger的out字段初始化的时候会抛出空指针异常。
有两种解决方法:
1. 提前定义:能够解决问题,但不是很漂亮
class SavingsAccount extends {
val filename = "savings.log"
} with Account with FileLogger {
...
}
在FileLogger被构造的时候,filename已经是初始化过的了
2. 懒值
trait FileLogger extends Logger{
val filename: String
lazy val out = new PrintStream(filename)
override def log(msg: String): Unit = {out.println(msg); out.flush()}
}
如此一来,out字段将在初次被使用时才会初始化。而在那个时候,filename字段应该已经被设好值了。不过,由于懒值在每次使用前都会检查是否已经初始化,它们用起来并不是那么高效。
原文:http://www.cnblogs.com/PaulingZhou/p/6657673.html