1多个trait的加载顺序
【码】讲解并示范
trait AA051 { println("AA051") }
trait AB051 { println("AA051") }
trait BA051 { println("BA051") }
trait BB051 { println("BB051") }
class AB extends AA051 with BA051 with AB051 with BB051 {
println("AB")
}
object Test21 {
def main(args: Array[String]): Unit = {
new AB()
}
}
2多层trait的加载顺序
先执行父类中的构造器,再执行子类的构造器:如果trait1也有自己的父类,要先执行父类构造器
trait A051 { println("A051")}
trait AA051 extends A051 { println("AA051") }
trait AB051 extends A051 { println("AA051") }
trait B051 { println("B051") }
trait BA051 extends B051 { println("BA051") }
trait BB051 extends B051 { println("BB051") }
class AB extends AA051 with BA051 with AB051 with BB051 {
println("AB")
}
object Test21 {
def main(args: Array[String]): Unit = {
new AB()
}
}
3空指针异常
trait FileLogger {
println(s"FileLogger ${filename}")
val filename:String
val fileout = new PrintWriter(filename)
def log(msg:String) = {
fileout.println(msg)
fileout.flush()
}
}
class Person051 extends FileLogger {
override val filename = "p051.log"
}
object Test19 {
def main(args: Array[String]): Unit = {
val p = new Person051()
p.log("person051 create log")
}
}
问题分析:通过打印,引导学生找到问题:调用p.log()时,fileName没有值。这就是继承时带的问题:先执行了trait构造器的代码,后执行了具体子类的构造器。而具体的赋值操作是在子类的构造器中才进行,所以,父类的filename没有值,导致空指针异常。 问题解决
方法1:懒加载
lazy val fileout = new PrintWriter(filename)
方法2:提前定义
val p = new {override val filename="p052.log"} with Person051
4trait与抽象类的区别
相同点:类和trait都可以定义成员变量(抽象,具体);继承时都使用extends关键字;
不同点:trait的构造器不能带参数;trait支持多继承;