(一) 多个 trait的加载顺序
一个类实现了多个特质之后,所涉及的多个构造器的执行顺序如何确定?
如果有多个父类,则按照从左到右的顺序调用。
代码:
object practice50 {
trait A {
println("A 特质构造器")
}
trait B {
println("B 特质构造器")
}
trait C {
println("C 特质构造器")
}
class Child extends C with B with A {
println("child ... ")
}
def main(args: Array[String]): Unit = {
val child = new Child()
}
}
输出结果为:
- C 特质构造器
- B 特质构造器
- A 特质构造器
- child ...
( 二 ) 多层 trait的加载顺序
先执行父类中的构造器,再执行子类的构造器:如果trait1也有自己的父类,要先执行父类构造器
代码:
trait AA {
println("AA 特质构造器")
}
trait A extends AA{
println("A 构造器")
}
trait B {
println("B 特质构造器")
}
trait CC {
println("CC 特质构造器")
}
trait C extends CC {
println("C 特质构造器")
}
输出结果:
- CC 特质构造器
- C 特质构造器
- B 特质构造器
- AA 特质构造器
- A 构造器
- child ...
(三)空指针异常
原代码:
trait FileLogger {
val filename: String
def writeLog(msg: String): Unit = {
writer.write(msg)
writer.close()
}
}
class MyWriter extends FileLogger {
// 继承三个特质a,b,c
override val filename: String = "test.log"
}
def main(args: Array[String]): Unit = {
val log = new MyWriter
log.writeLog("测试内容")
}
}
会出现nullpointerExcaption报错,主要原因是因为fillename是val未初始化
修改方式一:
trait FileLogger {
val filename: String
private lazy val writer: FileWriter = new FileWriter(filename)
def writeLog(msg: String): Unit = {
writer.write(msg)
writer.close()
}
}
用lazy val懒加载的方式初始化filename
修改方式二:
class MyWriter extends {
// 提前定义:在所有特质(FileLogger+TraitA/B/C)初始化前,赋值 filename
override val filename: String = "test.log"
} with FileLogger with TraitA with TraitB with TraitC {
// 类体无额外代码:filename 已通过提前定义完成初始化
}
用提前定义的方式直接赋值给filename
( 四 ) trait与类的区别
相同点:类和trait都可以定义成员变量(抽象,具体);继承时都使用extends关键字;
不同点:trait的构造器不能带参数;trait支持多继承;
class A{}
trait B{}
class AB extends A with B{
// 此处有编辑标记,原代码可能有省略
}