一、实现多个特质
格式:类名 extends 特质1 with 特质2 with 特质3 其中多个特质的顺序可以交换。
二、多个trait的加载顺序
一个类实现了多个特质之后,所涉及的多个构造器的执行顺序如何确定?
构造器的执行顺序:
- 1.先父 后子;
- 2.如果是多继承,有多个trait,按书写顺序从左到右。
trait AA {
println("AA 特质构造器")
}
trait A extends AA{
println("A 构造器")
}
trait CC {
println("CC 特质构造器")
}
trait B {
println("B 特质构造器")
}
trait C extends CC{
println("C 构造器")
}
// 继承 with
class Child() extends C with B with A{
println("child ...")
} // 继承三个特质A,B,C
三、空指针异常
以下代码有问题,大家先看看哪里有问题?
trait FileLogger {
val filename:String
val writer = new FileWriter(filename)
def writeLog(msg:String):Unit = {
writer.write(msg)
writer.close()
}
}
// 继承 with
class MyWriter extends FileLogger {
println("MyWriter")
override val filename: String = "test.log"
}
问题分析:通过打印,引导学生找到问题:调用test.log()时,fileName没有值。这就是继承时带的问题:先执行了trait构造器的代码,后执行了具体子类的构造器。而具体的赋值操作是在子类的构造器中才进行,所以,父类的filename没有值,导致空指针异常。
问题解决:
- 方法1:懒加载
lazy val writer = new PrintWriter(filename)
- 方法2:提前定义
val p = new {override val filename="test.log"} with Person051
四、trait与类的区别
不同点:trait的构造器不能带参数;trait支持多继承;
1.class 类。伴生类,抽象类,内部类。不能多继承。
2.trait 特质。可以多继承。构造器不能带参数。 extends with
相同点:类和trait都可以定义成员变量(抽象,具体);继承时都使用extends关键字;
1.都可以有:具体属性,抽象属性,具体方法,抽象方法。
2.都使用extends来做继承。
class A{}
trait B{}
class AB extends A with B{
}