本节主要内容
- trait构造顺序
- trait与类的比较
- 提前定义与懒加载
- trait扩展类
- self type
1 trait构造顺序
在前一讲当中我们提到,对于不存在具体实现及字段的trait,它最终生成的字节码文件反编译后是等同于java中的接口,而对于存在具体实现及字段的trait,其字节码文件反编译后得到的java中的抽象类,它有着scala语言自己的实现方式。因此,对于trait它也有自己的构造器,trait的构造器由字段的初始化和其它trait体中的语句构成,下面是其代码演示:
package cn.scala.xtwy
import java.io.PrintWriter
trait Logger{
println("Logger")
def log(msg:String):Unit
}
trait FileLogger extends Logger{
println("FilgeLogger")
val fileOutput=new PrintWriter("file.log")
fileOutput.println("#")
def log(msg:String):Unit={
fileOutput.print(msg)
fileOutput.flush()
}
}
object TraitDemo{
def main(args: Array[String]): Unit = {
//匿名类
new FileLogger{
}.log("trat demo")
}
}
//打印输出内容为:
Logger
FilgeLogger
//创建文件file.log,内容为
#
trat demo
通过上述不难发现,在创建匿名类对象时,先调用的是Logger类的构造器,然后调用的是FileLogger的构造器。实际上构造器是按以下顺序执行的:
1. 如果有超类,则先调用超类的构造器
2. 如果有父trait,它会按照继承层次先调用父trait的构造器
2. 如果有多个父trait,则按顺序从左到右执行
3. 所有父类构造器和父trait被构造完之后,才会构造本类
class Person
class Student extends Person with FileLogger with Cloneable
上述构造器的执行顺序为:
1 首先调用父类Person的构造器
2 调用父trait Logger的构造器
3 再调用trait FileLogger构造器,再然后调用Cloneable的构造器
4 最后才调用Student的构造器
2 trait与类的比较
通过前一小节,可以看到,trait有自己的构造器,它是无参构造器,不能定义trait带参数的构造器,即:
//不能定义trait带参数的构造器
trait FileLogger(msg:String)
除此之外 ,trait与普通的scala类并没有其它区别,在前一讲中我们提到,trait中可以有具体的、抽象的字段,也可以有具体的、抽象的方法,即使trait中没有抽象的方法也是合理的,如:
//FileLogger里面没有抽象的方法
trait FileLogger extends Logger{
println("FilgeLogger")
val fileOutput=new PrintWriter("file.log")
fileOutput.println("#")
def log